I think the problem is caused by dismount before async call finished.

const useAsync = () => {
  const [data, setData] = useState(null)
  const mountedRef = useRef(true)

  const execute = useCallback(() => {
    setLoading(true)
    return asyncFunc()
      .then(res => {
        if (!mountedRef.current) return null
        setData(res)
        return res
      })
  }, [])

  useEffect(() => {
    return () => { 
      mountedRef.current = false
    }
  }, [])
}

mountedRef is used here to indicate if the component is still mounted. And if so, continue the async call to update component state, otherwise, skip it.

This should be the main reason to not end up with a memory leak (access cleaned up memory) issue.

Demo

https://codepen.io/windmaomao/pen/jOLaOxO , fetch with useAsync https://codepen.io/windmaomao/pen/GRvOgoa , manual fetch with useAsync

Update

The above answer leads to the following component that we use inside our team.

/**
 * A hook to fetch async data.
 * @class useAsync
 * @borrows useAsyncObject
 * @param {object} _                props
 * @param {async} _.asyncFunc         Promise like async function
 * @param {bool} _.immediate=false    Invoke the function immediately
 * @param {object} _.funcParams       Function initial parameters
 * @param {object} _.initialData      Initial data
 * @returns {useAsyncObject}        Async object
 * @example
 *   const { execute, loading, data, error } = useAsync({
 *    asyncFunc: async () => { return 'data' },
 *    immediate: false,
 *    funcParams: { data: '1' },
 *    initialData: 'Hello'
 *  })
 */
const useAsync = (props = initialProps) => {
  const {
    asyncFunc, immediate, funcParams, initialData
  } = {
    ...initialProps,
    ...props
  }
  const [loading, setLoading] = useState(immediate)
  const [data, setData] = useState(initialData)
  const [error, setError] = useState(null)
  const mountedRef = useRef(true)

  const execute = useCallback(params => {
    setLoading(true)
    return asyncFunc({ ...funcParams, ...params })
      .then(res => {
        if (!mountedRef.current) return null
        setData(res)
        setError(null)
        setLoading(false)
        return res
      })
      .catch(err => {
        if (!mountedRef.current) return null
        setError(err)
        setLoading(false)
        throw err
      })
  }, [asyncFunc, funcParams])

  useEffect(() => {
    if (immediate) {
      execute(funcParams)
    }
    return () => {
      mountedRef.current = false
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return {
    execute,
    loading,
    data,
    error
  }
}

Update 2022

This approach has been adopted in the book https://www.amazon.com/Designing-React-Hooks-Right-Way/dp/1803235950 where this topic has been mentioned in useRef and custom hooks chapters, and more examples are provided there.

Update 2023

Google AI response: React 18 no longer shows a warning about memory leaks when you try to update the state of a component that has been removed/unmounted. This is because React 18 has improved its memory management so that it is less likely to cause memory leaks. However, there are still some cases where it is possible to cause a memory leak in React 18. One way to do this is to create an event listener that is not removed when the component unmounts. Another way to cause a memory leak is to use a ref that is not cleaned up when the component unmounts. If you are experiencing memory leaks in your React 18 application, you can use the React DevTools to track down the source of the leak. The React DevTools will show you which components are using the most memory and which components are not being unmounted properly. Once you have identified the source of the leak, you can fix it by removing the event listener or cleaning up the ref.

I created a pen to demo it but failed: https://codepen.io/windmaomao/pen/XWyLrOa?editors=1011

Answer from windmaomao on Stack Overflow
Top answer
1 of 11
110

I think the problem is caused by dismount before async call finished.

const useAsync = () => {
  const [data, setData] = useState(null)
  const mountedRef = useRef(true)

  const execute = useCallback(() => {
    setLoading(true)
    return asyncFunc()
      .then(res => {
        if (!mountedRef.current) return null
        setData(res)
        return res
      })
  }, [])

  useEffect(() => {
    return () => { 
      mountedRef.current = false
    }
  }, [])
}

mountedRef is used here to indicate if the component is still mounted. And if so, continue the async call to update component state, otherwise, skip it.

This should be the main reason to not end up with a memory leak (access cleaned up memory) issue.

Demo

https://codepen.io/windmaomao/pen/jOLaOxO , fetch with useAsync https://codepen.io/windmaomao/pen/GRvOgoa , manual fetch with useAsync

Update

The above answer leads to the following component that we use inside our team.

/**
 * A hook to fetch async data.
 * @class useAsync
 * @borrows useAsyncObject
 * @param {object} _                props
 * @param {async} _.asyncFunc         Promise like async function
 * @param {bool} _.immediate=false    Invoke the function immediately
 * @param {object} _.funcParams       Function initial parameters
 * @param {object} _.initialData      Initial data
 * @returns {useAsyncObject}        Async object
 * @example
 *   const { execute, loading, data, error } = useAsync({
 *    asyncFunc: async () => { return 'data' },
 *    immediate: false,
 *    funcParams: { data: '1' },
 *    initialData: 'Hello'
 *  })
 */
const useAsync = (props = initialProps) => {
  const {
    asyncFunc, immediate, funcParams, initialData
  } = {
    ...initialProps,
    ...props
  }
  const [loading, setLoading] = useState(immediate)
  const [data, setData] = useState(initialData)
  const [error, setError] = useState(null)
  const mountedRef = useRef(true)

  const execute = useCallback(params => {
    setLoading(true)
    return asyncFunc({ ...funcParams, ...params })
      .then(res => {
        if (!mountedRef.current) return null
        setData(res)
        setError(null)
        setLoading(false)
        return res
      })
      .catch(err => {
        if (!mountedRef.current) return null
        setError(err)
        setLoading(false)
        throw err
      })
  }, [asyncFunc, funcParams])

  useEffect(() => {
    if (immediate) {
      execute(funcParams)
    }
    return () => {
      mountedRef.current = false
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return {
    execute,
    loading,
    data,
    error
  }
}

Update 2022

This approach has been adopted in the book https://www.amazon.com/Designing-React-Hooks-Right-Way/dp/1803235950 where this topic has been mentioned in useRef and custom hooks chapters, and more examples are provided there.

Update 2023

Google AI response: React 18 no longer shows a warning about memory leaks when you try to update the state of a component that has been removed/unmounted. This is because React 18 has improved its memory management so that it is less likely to cause memory leaks. However, there are still some cases where it is possible to cause a memory leak in React 18. One way to do this is to create an event listener that is not removed when the component unmounts. Another way to cause a memory leak is to use a ref that is not cleaned up when the component unmounts. If you are experiencing memory leaks in your React 18 application, you can use the React DevTools to track down the source of the leak. The React DevTools will show you which components are using the most memory and which components are not being unmounted properly. Once you have identified the source of the leak, you can fix it by removing the event listener or cleaning up the ref.

I created a pen to demo it but failed: https://codepen.io/windmaomao/pen/XWyLrOa?editors=1011

2 of 11
46

useEffect will try to keep communications with your data-fetching procedure even while the component has unmounted. Since this is an anti-pattern and exposes your application to memory leakage, cancelling the subscription to useEffect optimizes your app.

In the simple implementation example below, you'd use a flag (isSubscribed) to determine when to cancel your subscription. At the end of the effect, you'd make a call to clean up.

export const useUserData = () => {
  const initialState = {
    user: {},
    error: null
  }
  const [state, setState] = useState(initialState);

  useEffect(() => {
    // clean up controller
    let isSubscribed = true;

    // Try to communicate with sever API
    fetch(SERVER_URI)
      .then(response => response.json())
      .then(data => isSubscribed ? setState(prevState => ({
        ...prevState, user: data
      })) : null)
      .catch(error => {
        if (isSubscribed) {
          setState(prevState => ({
            ...prevState,
            error
          }));
        }
      })

    // cancel subscription to useEffect
    return () => (isSubscribed = false)
  }, []);

  return state
}

You can read up more from this blog juliangaramendy

🌐
DEV Community
dev.to › elijahtrillionz › cleaning-up-async-functions-in-reacts-useeffect-hook-unsubscribing-3dkk
Cleaning up Async Functions in React's useEffect Hook (Unsubscribing) - DEV Community
December 2, 2021 - If you are wondering, "why does it only happen with async functions or tasks"? Well, that's because of the JavaScript event loop. If you don't know what that means, then check out this YouTube Video by Philip Roberts. Thanks for reading. I hope to see you next time. Please kindly like and follow me on Twitter @elijahtrillionz to stay connected. ... Software engineer, author, blogger, lifelong learner. ... Using an isMounted approach is somewhat of an antipattern, whenever possible it's better to cancel the request (in the fetch example, you can use AbortController to cancel the request).
Discussions

To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function (potential async issue)
Getting the following (common) message in my console: 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 ... More on stackoverflow.com
🌐 stackoverflow.com
reactjs - Cancel all subscriptions and asynchronous tasks in a useEffect cleanup function - What am I doing wrong? - Stack Overflow
I am trying to change the button based on if a user is logged in or not. the functionality is managed from MyNav.js. if the user is logged in I show the sign-out button, otherwise, I show the sign-... More on stackoverflow.com
🌐 stackoverflow.com
React - To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function
I get the following 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. More on stackoverflow.com
🌐 stackoverflow.com
December 14, 2020
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function
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 tas... More on github.com
🌐 github.com
0
November 2, 2021
🌐
GitHub
github.com › facebook › react › issues › 14227
Warning: ... To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. · Issue #14227 · facebook/react
November 13, 2018 - 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.
🌐
DEV Community
dev.to › devcse › how-to-cancel-all-subscriptions-and-asynchronous-tasks-in-a-useeffect-cleanup-function-4n1l
How to cancel all subscriptions and asynchronous tasks in a useEffect cleanup function? - DEV Community
July 20, 2021 - Even if the component is unmounted, useEffect will try to maintain contact with your data-fetching function. Because this is an anti-pattern that exposes your app to memory leaks, canceling your useEffect subscription optimizes your app. When you don’t use you useEffect hook effectively and dont perform any cleanup technique then the error looks like in the console:
🌐
LogRocket
blog.logrocket.com › home › understanding react’s useeffect cleanup function
Understanding React’s useEffect cleanup function - LogRocket Blog
December 16, 2024 - Now that we understand how to make useEffect run once, let’s get back to our cleanup function conversation. The cleanup function is commonly used to cancel all active subscriptions and async requests.
🌐
DEV Community
dev.to › otamnitram › react-useeffect-cleanup-how-and-when-to-use-it-2hbm
React useEffect cleanup: How and when to use it - DEV Community
May 14, 2020 - This is useful besides the cleanup function to prevent memory leaking. useEffect(() => { const source = axios.CancelToken.source() const fetchUsers = async () => { try { await Axios.get('/users', { cancelToken: source.token, }) // ...
🌐
Tasos Kakouris
tasoskakour.com › blog › react-use-effect-cleanups
React & useEffect cleanups | Tasos Kakouris
December 27, 2021 - index.js:27 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.
🌐
Juliangaramendy
juliangaramendy.dev › blog › use-promise-subscription
Cancelling a Promise with React.useEffect - Julian​Garamendy​.dev
April 7, 2019 - 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.
Find elsewhere
🌐
Stack Overflow
stackoverflow.com › questions › 66064885 › cancel-all-subscriptions-and-asynchronous-tasks-in-a-useeffect-cleanup-function
reactjs - Cancel all subscriptions and asynchronous tasks in a useEffect cleanup function - What am I doing wrong? - Stack Overflow
... 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.
🌐
BinarApps
binarapps.com › all posts › technology › clean up request in useeffect react hook
Clean up request in useEffect React Hook - BinarApps
May 15, 2022 - Anytime the effect is no longer valid, for example when a component using that effect is unmounting, this function is called to clean everything up. In our case, it is very helpful. Changing from one component to another will unmount the first one. During unmounting the component `useEffect` will call `abort()` method from `AbortController` to tell `fetch` and later the browser that this request is not needed anymore and can be canceled.
🌐
Ryan Neilson
neilsonwebdesign.com › home › blog › cleanup functions in react useeffect
Cleanup Functions in React useEffect - Ryan Neilson
May 30, 2023 - useEffect(() => { // Do the effect here return () => { // Do the cleanup here } }, []) In useEffect functions where we’ve added a setTimeout call, we can clean up after this by using the built-in clearTimeout function: useEffect(() => { let example = setTimeout(() => { // Do the effect here ...
🌐
Tommybernaciak
tommybernaciak.com › cleanup-requests-useeffect
Clean up request in the useEffect React Hook
Anytime the effect is no longer valid anymore, for example when component using that effect is unmounting, this function will be called to clean everything. In our case, it is very helpful. Changing from one component to another will unmount the first one. During unmounting the component useEffect will call abort() method from AbortController to tell fetch and later the browser that this request is not needed anymore and can be canceled.
🌐
GitHub
github.com › contentstack › micro-frontend-example › issues › 10
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function · Issue #10 · contentstack/micro-frontend-example
November 2, 2021 - 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. in bound...
Author   praason
🌐
CopyProgramming
copyprogramming.com › howto › cancel-all-subscriptions-in-a-useeffect-cleanup-function-created-by-context-consumer
Reactjs: Utilize useEffect cleanup function from Context.Consumer to cancel subscriptions
April 20, 2023 - A cautionary note states that updating ... in your application. To resolve this issue, it is recommended that you terminate all subscriptions and asynchronous tasks while in a useEffect cleanup function context....
🌐
CodingDeft
codingdeft.com › posts › react-prevent-state-update-unmounted-component
Can't perform a react state update on an unmounted component | CodingDeft.com
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.
🌐
Reddit
reddit.com › r/reactnative › i keep 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
r/reactnative on Reddit: I keep 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
March 15, 2022 -

What do they mean by this? (Im new to RN)

Here is all my useEffect hooks:

-App.js:

useEffect(() => {
    auth.onAuthStateChanged((userCred) => {
        if (userCred && userCred !== null){
          setIsLoading(false)
          setShowOnboard(false)
        } else{
          setIsLoading(false)
          setShowOnboard(true)
        }
      })
    },[])

-Home.js:

useEffect(() => {
        const getData = async () => {
            getDocs(docRef).then(res => {
                let temp = []
                res.docs.forEach(doc => {
                    temp.push({
                        ...doc.data(),
                        key: doc.id
                    });
                });
                let otherTemp = []
                temp.forEach(doc => {
                    otherTemp.push(doc.item)
                    setData(otherTemp)
                })

            }).catch(e => {
                console.log(e);
            })

        }

        getData();

    }, [])

-IntroductionSlides:

useEffect(() => {
      getIntroVar().then(res => {
        console.log("res",res);
        if (res === "yes"){
          console.log("log in page");
          setShowOnboard(false)
        } else {
          console.log("intro page");
          setShowOnboard(true)
        }
      })
    },[])
🌐
Apollo Community
community.apollographql.com › client sdks
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 - Client SDKs - Apollo Community
July 29, 2021 - My page / page level query is rendered SSR. I have a below the fold component on my page that is not SSR (has a component level useQuery hook with the ssr: false option). It accepts an itemId prop that is handed down from the page level query’s results. The itemId prop is used as a variable in the component level query.