skip to Main Content

In the below code, I expect it to run

start of useEffect

false

cleanup

start of useEffect

true

but it instead returns this, where isCanceled is still false even after the cleanup

start of useEffect

false

cleanup

start of useEffect

false

The code is this:

  useEffect(() => {
    console.log("start of useEffect")
    let isCanceled = false;
    console.log(isCanceled);

    return () => {
      isCanceled = true;
      console.log("cleanup");
    };
  });

Why is it still false even after cleanup? I was following this article for your reference https://dev.to/pallymore/clean-up-async-requests-in-useeffect-hooks-90h

Tried to reread article and google this question, but not sure why still.

2

Answers


  1. Nope, you’ve got it wrong.

    isCanceled is a local variable to every useEffect run. It’s not shared between runs of the useEffect callback.

    The cleanup function of the useEffect only sets the isCanceled variable in it’s local scope to false.

    The running of the cleanup function doesn’t affect the local variable isCanceled of any other run of the callback passed to useEffect

    Login or Signup to reply.
  2. Cleanup will change ‘isCanceled’ for previous callBack if not resolved yet but won’t effect the new callBack, as it is within useEffect callBack scope.

    After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. After your component is removed from the DOM, React will run your cleanup function

    There are scenarios where useEffect might be called multiple times, even if it is supposed to run only once with an empty array of dependencies []. The cleanup function will be called every time useEffect is triggered again and could help to avoid multi-call side effects.

    In the example below, the result of the first request will be ignored:

    callDelay is changed by setCallDelay(6000) in if (!count) which triggered useEffect before the first request is delivered.

    const request = async (callDelay) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve("done!");
        }, callDelay);
      });
    };
    
    let count;
    const App = () => {
      const [callDelay, setCallDelay] = useState(5000);
    
      if (!count) {
        count = 1;
        setTimeout(() => {
          setCallDelay(6000);
        }, 1000);
      }
    
      useEffect(() => {
        let isCanceled = false;
    
        request(callDelay).then((result) => {
          if (!isCanceled) {
            console.log(result);
          } else {
            console.log("Too late!, reulst arrived but meanwhile useEffect was called again.");
          }
        });
    
        return () => {
          isCanceled = true;
        };
      }, [callDelay]);
    
      return <Text>Called with {callDelay} ms delay</Text>;
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search