JaeLeeSo
JaeLeeSo

Reputation: 263

React useCallback memory leak unmounted component

I'm new to React and I'm getting this error:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

From what I understand, it appears as though I have a memory leak and need a useEffect cleanup for my useCallback hook?

I have tried adding a useRef to check mount but the return doesn't change the state to false.


const MyComponent = ({data}) => {
  const mounted = useRef(true);
  const [loading, setLoading] = useState(false);

  const isLoading = useCallback(async () => {
    setLoading(true);

    if (data) {
      console.log('YAY DATA: ', data);
    }

    return () => {
      setLoading(false); // unreachable code
      mounted.current = false; // does this do the cleanup?
    };
  }, [loading]);

  return (
    //...some component
  );
};

export default MyComponent;

Upvotes: 12

Views: 15244

Answers (2)

dglozano
dglozano

Reputation: 6607

If you perform an async operation that, on completion, needs to set something to the state, you must make sure that the component is still mounted before updating the state.

In order to do so, you try something like this:

const MyComponent = (props) => {
  const mounted = useRef(false);

  useEffect(() => {
        mounted.current = true; // Will set it to true on mount ...
        return () => { mounted.current = false; }; // ... and to false on unmount
  }, []);

  const [loading, setLoading] = useState(false);
  const [data, setData] = useState([]);

  const myAsyncCallback = useCallback(async () => {
    setLoading(true);

    // Await for an async function...
    const data = await yourAsyncFunction(...); // Complete with proper code here

    // While waiting for yourAsyncFunction to be done,
    // It's possible that your component had been unmounted.

    // Therefore, you have to check if the component is still mounted before updating states
    if (mounted.current) { 
      setLoading(false);
      setData(data);
    }
  }, []);

  return (
    //...some component that calls myAsyncCallback
  );
};

export default MyComponent;

Upvotes: 26

anonkey
anonkey

Reputation: 102

As useEffect cleanUp on component unmount, you can not update the state (and since it unmounts where value could be stored)

Your code seem to be a code where api call is done in the useEffect .like that :

useEffect(() => {
    let mounted = true
    fetchAPI.then(() => {
        if (mounted) {
            setloading(false)
        }
    })

    return function cleanup() {
        mounted = false
    }
}, [])

If it's that look at here : https://dev.to/otamnitram/react-useeffect-cleanup-how-and-when-to-use-it-2hbm

Upvotes: 1

Related Questions