Languages
[Edit]
EN

React - custom async useEffect hook

9 points
Created by:
Kate_C
21370

In this article, we would like to show you how to create your own async useEffect hook function in React.

By default, React doesn't provide that kind of function, but there is a simple way how to create own one.

The main advantage of that kind function is the possibility to use await keyword when we do AJAX or other async operations with useEffect, that makes code more readable. In the below approach, we can use easily isMount() function that checks if the component state can be modified still - after AJAX request is done component can be unmounted that requires to stop all actions on our component.

Quick solution:

const useAsyncEffect = (callback, dependences) => {
  	React.useEffect(() => {
      	let mounted = true;
      	let cleanup = null;
      	const promise = callback(() => mounted);
      	if (promise instanceof Promise) {
          	promise
              	.then(result => {
              		if (mounted) {
                    	cleanup = result;
                    } else {
                        result?.();
                    }
                })
                .catch(console.error);
        } else {
          	cleanup = promise;
        }
      	return () => {
            mounted = false;
          	cleanup?.();
        };
    }, dependences);
};

// Usage example:

useAsyncEffect(async (isMounted) => {
    console.log('MyComponent mounted...');
  	//TODO: some async operations here...
    if (isMounted()) {
        //TODO: we can update component state here if still is mounted...
    }
    return () => {
        console.log('MyComponent unmounted...');
    };
});

useAsyncEffect with AJAX example

In this section, we would like to show you how to use useAsyncEffect with fetch function (with AJAX requests).

Note: in below example we can check isMount() status always after any method with await keyword is called to finish useEffect logic execution, but most important is to do not change state on unmounted compoennt what we do.

// ONLINE-RUNNER:browser;

//Note: Uncomment import lines during working with JSX Compiler.
// import React from "react";
// import ReactDOM from "react-dom";

const useAsyncEffect = (callback, dependences) => {
  	React.useEffect(() => {
      	let mounted = true;
      	let cleanup = null;
      	const promise = callback(() => mounted);
      	if (promise instanceof Promise) {
          	promise
              	.then(result => {
              		if (mounted) {
                    	cleanup = result;
                    } else {
                        result?.();
                    }
                })
                .catch(console.error);
        } else {
          	cleanup = promise;
        }
      	return () => {
            mounted = false;
          	cleanup?.();
        };
    }, dependences);
};

// Usage example:

const MyComponent = ({requestText}) => {
	const [response, setResponse] = React.useState();
    useAsyncEffect(async (isMounted) => {
  		console.log('MyComponent mounted...');
      	try {
            const safeRequestText = encodeURIComponent(requestText);
      		const response = await fetch(`/examples/echo?text=${safeRequestText}`);
          	const responseText = await response.text();
          	if (isMounted()) {
          		setResponse(`Response: ${responseText}`);
            }
        } catch (error) {
          	if (isMounted()) {
          		setResponse('Request error!');
            }
        }
      	return () => {
        	console.log('MyComponent unmounted...');
        };
    }, []);
    return (
      <div>{response}</div>
    );
};

const App = () => {
    const [visible, setVisible] = React.useState(false);
  	const createHandleClick = (timeout) => {
    	return () => {
        	setVisible(true);
            setTimeout(() => setVisible(false), timeout);
        };
    };
    return (
      <div>
        <div>
          <span>Mount MyComponent and later unmount </span>
          {' '}
          <button disabled={visible} onClick={createHandleClick()}>
             immediately
          </button>
          {' or '}
          <button disabled={visible} onClick={createHandleClick(1000)}>
            after 1s
          </button>
        </div>
        {visible && <MyComponent requestText="Hi there!" />}
      </div>
    );
};

const root = document.querySelector('#root');
ReactDOM.render(<App />, root);

useAsyncEffect as timer example

async useEffect can be easy way used to write own timer logic. In the below example we used sleep() the method that slows down useAsyncEffect function for 1 second.

// ONLINE-RUNNER:browser;

//Note: Uncomment import lines during working with JSX Compiler.
// import React from "react";
// import ReactDOM from "react-dom";

const useAsyncEffect = (callback, dependences) => {
  	React.useEffect(() => {
      	let mounted = true;
      	let cleanup = null;
      	const promise = callback(() => mounted);
      	if (promise instanceof Promise) {
          	promise
              	.then(result => {
              		if (mounted) {
                    	cleanup = result;
                    } else {
                        result?.();
                    }
                })
                .catch(console.error);
        } else {
          	cleanup = promise;
        }
      	return () => {
            mounted = false;
          	cleanup?.();
        };
    }, dependences);
};

// Usage example:

const sleep = (time) => new Promise(resolve => setTimeout(resolve, time));

const App = () => {
    const [counter, setCounter] = React.useState(0);
  	useAsyncEffect(async (isMounted) => {
      	console.log('App mounted or counter changed...');
      	await sleep(1000);
      	setCounter(counter + 1);
      	return () => {
        	console.log('App unmounted or counter changed...');
        };
    }, [counter]);
    return (
      <div>{counter}</div>
    );
};

const root = document.querySelector('#root');
ReactDOM.render(<App />, root);
Native Advertising
­čÜÇ
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

ÔŁĄ´ŞĆ­čĺ╗ ­čÖé

Join