Search code examples
reactjspromiseuse-effect

Correct way to cleanup useEffect with Promise


I have useEffect callback where I'm fetching some data from several API. To do that I'm using Promise and my code looks like that:

useEffect(() => {
   const fetchAllData = async () => {
      const resourceOne = new Promise((resolve) => {
         // fetching data from resource one and changing some states
      })
      const resourceTwo = new Promise((resolve) => {
         // fetching data from resource two and changing some states
      })
      const resourceThree = new Promise((resolve) => {
         // fetching data from resource three and changing some states
      })

      await Promise.all([resourceOne, resourceTwo, resourceThree])
      .then(() => {
         // changing some states
      })
   }

   return fetchAllData()
},[])

How I understand in this situation useEffect unmount before fetching all data and then gives me an warning

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.

So how to write correctly cleanup code to avoid this warning?


Solution

  • You can use a boolean and toggle it in the cleanup function (the function that is returned in the callback passed to useEffect)

    useEffect(() => {
        let shouldUpdate = true;
    
        const fetchAllData = () => {
           const resourceOne = new Promise((resolve) => {
              // fetching data from resource one and changing some states
           })
           const resourceTwo = new Promise((resolve) => {
              // fetching data from resource two and changing some states
           })
           const resourceThree = new Promise((resolve) => {
              // fetching data from resource three and changing some states
           })
     
           Promise.all([resourceOne, resourceTwo, resourceThree])
            .then(() => {
               if(shouldUpdate) {
                   // update state
               }
           })
        }
     
        fetchAllData()
        return () => {
            shouldUpdate = false;
        }
     },[])
    

    If the component is unmounted the cleanup function will be called and shouldUpdate will change to false. When the promises resolve, the state will not update as shouldUpdate is no longer true.