Ansul
Ansul

Reputation: 420

useEffect cleanup function in react

I get this 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.'

Code

 const [photo, setPhoto] = useState([]);
 useEffect(() => {
 fetch('/mypost', {
  headers: {
    cookie: 'access_token',
  },
})
  .then((res) => res.json())
  .then((data) => {
    setPhoto(data.mypost);
  });
}, []);

The data is fetched but I could'nt figure what to add in the clean up. Any suggestions?

Upvotes: 0

Views: 1587

Answers (3)

oreopot
oreopot

Reputation: 3450

try the following code:

 useEffect(() => {
let isMounted = true;
 fetch('/mypost', {
  headers: {
    cookie: 'access_token',
  },
})
  .then((res) => res.json())
  .then((data) => {
    if (isMounted)  setPhoto(data.mypost);
  });

//cleanup function
return () => { isMounted = false };
    
}, []);

Upvotes: 1

Dmitriy Mozgovoy
Dmitriy Mozgovoy

Reputation: 1597

Generic JSON fetch demo

import React, {useState} from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpFetch from "cp-fetch";

export default function TestComponent(props) {
  const [photo, setPhoto] = useState([]);

  useAsyncEffect(
    function* () {
      const response = yield cpFetch('/mypost');
      setPhoto((yield response.json()).mypost);
    },
    []
  );

  return <div></div>;
}

Upvotes: 1

Drew Reese
Drew Reese

Reputation: 202751

Issue

The problem is that the fetch request resolves but the component has unmounted (for some reason) and so the state update can't occur now.

Solution

You need to use an Abort Controller to cancel in-flight requests. If the component unmounts, an effect cleanup function cancels the fetch request.

useEffect(() => {
  const controller = new AbortController(); // <-- create controller
  const { signal } = controller; // <-- get signal for request

  fetch('/mypost', {
    headers: {
      cookie: 'access_token',
    },
    signal, // <-- pass signal with options
  })
    .then((res) => res.json())
    .then((data) => {
      setPhoto(data.mypost);
    });

  return () => controller.abort(); // <-- return cleanup function to abort
}, []);

Note: When abort() is called, the fetch() promise rejects with an AbortError.

You will likely need to catch this promise rejection somewhere. You can append a .catch block to the Promise chain.

  fetch('/mypost', {
    headers: {
      cookie: 'access_token',
    },
    signal, // <-- pass signal with options
  })
    .then((res) => res.json())
    .then((data) => {
      setPhoto(data.mypost);
    })
    // catch any rejected fetch promises (including aborted fetches!)
    .catch(console.error);

Upvotes: 3

Related Questions