Yuriy Yudin
Yuriy Yudin

Reputation: 123

React cleanup function doesn't clean state

experts, please explain me, why in the following code the state of the property won't be cleaned in the useEffect cleanup function?

My component:

export default function TestComp() {

  let { id } = useParams();
  const [value, setValue] = useState(null);
  console.log('[TestComp] called...');

  const cleanup = () => {
    console.log('[TestComp] old value', value);
    setValue(null);
  };

  useEffect(() => {
    console.log('[TestComp] id changed: ', id);
    console.log('[TestComp] value in useEffect', value);
    setValue(id);
    return () => {
      cleanup();
    }
  }, [id]);

  return (<React.Fragment>Test id: {id}</React.Fragment>)
}

Console output:

[TestComp] called... TestComp.js:8
[TestComp] old value satellites:MTP TestComp.js:11
[TestComp] id changed:  satellites:MTP TestComp.js:16
[TestComp] value in useEffect satellites:FPGA TestComp.js:17
[TestComp] called... 2 TestComp.js:8
[TestComp] old value satellites:FPGA TestComp.js:11
[TestComp] id changed:  satellites:FNE TestComp.js:16
[TestComp] value in useEffect satellites:MTP TestComp.js:17
[TestComp] called... TestComp.js:8

I expect that, when useEffect will be called for the 2. time, the value wull be cleaned up and is null, but it still keeps the old value:

value in useEffect satellites:MTP TestComp.js:17

Thank you in advance.

Upvotes: 3

Views: 3982

Answers (3)

Azeem Chauhan
Azeem Chauhan

Reputation: 467

Return function from useEffect just cleans up the previous effects before applying the next effects. But main problem in your code is

    const cleanup = () => {
    console.log('[TestComp] old value', value);
    setValue(null); // This is not prefer way to use setValue here.
}

Normally, During cleans up we unsubscribe from external service/subscription but here you are changing the state which make no sense here and immediately getting update by useEffect setValue that run and just after setValue inside cleanup called which is also reason of calling effect again,

Check your code after adding setTimeout in your useEffect.

`useEffect(() => {
    console.log('[TestComp] id changed: ', id);
    console.log('[TestComp]:Effect value in useEffect', value);
    setValue(id);
    return () => {
      setTimeout(()=> cleanup(), 5000)
    }
  }, [id]);`

Possible Solution -

  • In above case you are using id, lift up this property to parent component and pass it as property to TestComp component.

  • when effect runs whole component get re-render and scope get destroyed but all state maintain inside the closure of useState hooks.

working example for above situation

Upvotes: 1

Dennis Vash
Dennis Vash

Reputation: 53884

You might want to add another useEffect, because, in the current situation, the cleanup function will run only on unmount which is pretty useless for the current logic.

export default function TestComp() {
  let { id } = useParams();
  const [value, setValue] = useState(null);
  console.log('[TestComp] called...');

  useEffect(() => {
    console.log('[TestComp] id changed: ', id);
    console.log('[TestComp] value in useEffect', value);
    setValue(id);

    /* v Useless cleanup because the component unmounts anyway,
         the `value` state will be cleaned automatically.
    return () => {
      cleanup();
    }
    */
  }, [id]);

  // ^ Firstly, the `value` changed on id change
  // v Secondly, the `value` will be reset on `value` change
  useEffect(() => {
    console.log('[TestComp] old value', value);
    setValue(null);
  }, [value]);

  return <>Test id: {id}</>;
}

Upvotes: 1

pjoe
pjoe

Reputation: 326

I think it's a matter of the sequence in which things happen.

useEffect will 'capture' value as part of a closure when it is called, i.e. when the TestComp function is called. When id changes React will schedule a call to the cleanup function and then a call to the effect. However TestComp is not called again until after the new effect function has been called.

You can see this in the log as each old value from the cleanup function is immediately followed by id changed from the effect function.

And because the new effect functions 'captures' value from when TestComp was called, it will not see the value set by the cleanup function.

Also note, at least from testing I saw React complain about value and cleanup not being in the dependencies for useEffect.

Upvotes: 0

Related Questions