Memory leak message when i'm using history.push inside useEffect

Viewed 71

When I'm passing history.push in a UseEffect function.

function Home(props) {
  useEffect(() => {
    const fetchData = async () => {
      const response = await listingService.allListingDetails(data.listingId);

      let tasksReceived = response.data.tasks;
      let tasks = [...tasksReceived];
      setTasks(tasks);
      setListing(response.data);

      if (tasks.length < 1) {
        history.push({
          pathname: "/firstpage",
          state: {
            listing: response.data,
          },
        });

        return;
      }
    };
  }, [changeState]);
}

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. at Home (http://localhost:3001/static/js/main.chunk.js:11116:79)

If I'm commenting the below line, the memory leak error doesn't come anymore.

      if (tasks.length < 1) {
        history.push({
          pathname: "/firstpage",
          state: {
            listing: response.data,
          },
        });
2 Answers

That's probably because react is performing a state update at the same moment that you're trying to navigate (history.push). Try separating the code (the fetching in one side, the state update in another...) and return the history.push (cleanup function).

function Home(props) {

  useEffect(() => {
      const fetchData = async () => {
        try {
          const response = await listingService.allListingDetails(data.listingId);
          let tasksReceived = response.data
          return tasksReceived;
        } catch (e) {
            console.error(e)
          return null;
        }
      }

      const data = fetchData().then((data) => data);
      setTasks(data?.tasks || []);
      setListing(data || {});
      

      if (data?.tasks.length < 1) {
        return () => history.push({
          pathname: "/firstpage",
          state: {
            listing: response.data,
          },
        });
      }
  }, [changeState]);
}

My guess is that react is trying to apply the state updates of setTasks and setListing after the redirection happens and the component is already unmounted, remember that state updates are not immediate but batched. Try reordering the logic by setting the state only if you have tasks since updating the state when you're redirecting is meaningless.

    const fetchData = async () => {
      const response = await listingService.allListingDetails(data.listingId);
      let tasksReceived = response.data.tasks;
      let tasks = [...tasksReceived];
      if (tasks.length < 1) {
        history.push({
          pathname: "/firstpage",
          state: {
            listing: response.data,
          },
        });
      } else {
        setTasks(tasks);
        setListing(response.data);
      }
    };
Related