How to cancel all subscriptions and asynchronous tasks in a useEffect cleanup function?

  • Post author:
  • Post category:Bugs / React Js
  • Post last modified:July 7, 2021
  • Reading time:2 mins read

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:

React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing….

To fix this problem, given below some example code, that might be help:

Example Code 1(Solution):

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.

useEffect(() => {
  // set a clean up flag
  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)
}, []);

Example Code 2(Solution):

import React,{useEffect} from 'react';

const App = () => {
  const [ data, setData ] = React.useState([]);

useEffect(() => {
  const abortController = new AbortController();
  
  void async function fetchData() {
    try {
        const url = 'https://jsonplaceholder.typicode.com/todos/1';
        const response = await fetch(url, { signal: abortController.signal });
        setData(await response.json());
    } catch (error) {
        console.log('error', error);
    }
}();

  // cancel subscription to useEffect
  return () => {
    abortController.abort(); // cancel pending fetch request on component unmount
  }
}, []);

return <pre>{JSON.stringify(data, null, 2)}</pre>;

}

export default App

Example Code 3(Solution):

const [data, setData] = useState(null);
const [posts, setPosts] = useState(null);
/* This would be called on initial page load */
useEffect(()=>{
    fetch(`https://www.reddit.com/r/${subreddit}.json`)
    .then(data => {
        setData(data);
    })
    .catch(err => {
        /* perform error handling if desired */
    });
}, [])

/* This would be called when store/state data is updated */
useEffect(()=>{
    if (data) {
        setPosts(data.children.map(it => {
            /* do what you want */
        }));
    }
}, [data]);

Hope it will help!