The hooks introduced with React 16.8 are a nice way to control state and other features on functional React components. They discard the necessity of third-party libraries such as Recompose – which is the inspiration for React hooks, by the way.

One way one can apply the useEffect hook, for example, is to mimic the behavior of componentDidMount event to bind a value returned by an asynchronous function to a given state. Like:

const React, { useEffect, useState } from 'react';

export default Component = () => {
    const [ data, setData ] = useState(null);
    useEffect(() => {
        (async () {
            setData(await someAsyncApiCall());
        })();
    }, []);
    return (<span>{ data }</span>);

Calling the useEffect hook with an empty array will make it be called only when the component was mounted, i.e. is the same as componentDidMount event.

However, if the component has unmounted some point before the async call finishes, it’s likely to get the following error in the console:

Can’t perform a React state update on an unmounted component

That happens because the async function called setData, which is the state setter, after the component has unmounted. To fix that error, it’s required to cancel the async function before changing the state in case the component did unmount.

In a “regular” React component, there is the componentWillUnmount event. One could set a flag to indicate the component is not mounted anymore to avoid setting the state late. Simulating this same approach with hooks is not hard. A function returned by a useEffect hook with an empty array in the second argument (the case above) run when the component will unmount. So, one could have the following:

const React, { useEffect, useState } from 'react';

export default Component = () => {
    const [ data, setData ] = useState(null);
    useEffect(() => {
        let mounted = true; // Indicate the mount state
        (async () {
            const data = await someAsyncApiCall();
            if (!mounted) return; // Exits if comp. is not mounted
            setData(data);
        })();
        return () => { // Runs when component will unmount
            mounted = false;
        };
    }, []);
    return (<span>{ data }</span>);

Now the error is gone since the state is not updated if the component is not mounted anymore.

I particularly am fond of hooks. I used to add Recompose to projects in the past and like the way the code looks with functional components. There is also, the performance benefits.

If you have questions or suggestions, feel free to express yourself in the comments below.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.