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
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
One or more of your useEffect functions are trying to do a state update after the component is unmounted (meaning that it is not being rendered by the DOM anymore). So basically the the component the useEffect tries to do a state change in is not there anymore to do the state change. Example: (without knowing the code hard to know for sure, but as an example) Your IntroductionSlides is mounted. The useEffect function triggers getIntroVar(). IntroductionSlides is unmounted for some reason. GetIntroVar() finishes after this and tries to setShowOnboard(). However since the component is unmounted the there is no state to be set, hence the error. You can either cancel the action in a useEffect cleanup. So when the component unmounts, provide a function that will cancel the given action. You have asynchronous functions that might be the root cause here. You should cancel those with providing the cancel function in useEffects return () =>. Or make sure that the component will not be unmounted before the your functions have completed in your useEffects. More on reddit.com
🌐 r/reactnative
1
5
March 15, 2022
reactjs - How to cancel all subscriptions and asynchronous tasks in a useEffect cleanup function - 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 useEf... More on stackoverflow.com
🌐 stackoverflow.com
🌐
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
🌐
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.
🌐
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.
🌐
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.
🌐
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.
🌐
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....
🌐
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
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...
🌐
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)
        }
      })
    },[])
🌐
Stack Overflow
stackoverflow.com › questions › 67947553 › how-to-cancel-all-subscriptions-and-asynchronous-tasks-in-a-useeffect-cleanup-fu
reactjs - How to cancel all subscriptions and asynchronous tasks in a useEffect cleanup function - 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.
🌐
GitHub
github.com › learn-co-curriculum › react-hooks-use-effect-cleanup
GitHub - learn-co-curriculum/react-hooks-use-effect-cleanup
index.js:1 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.
Starred by 6 users
Forked by 2.1K users
Languages   HTML 64.1% | JavaScript 35.9% | HTML 64.1% | JavaScript 35.9%