Michael
Michael

Reputation: 5048

how to use the useEffect hook on component unmount to conditionally run code

For some odd reason the value of props in my "unmount" useEffect hook is always at the original state (true), I can console and see in the devtools that it has changed to false but when the useEffect is called on unmount it is always true.
I have tried adding the props to the dependancies but then it is no longer called only on unmount and does not serve it's purpose.
Edit: I am aware the dependancy array is empty, I cannot have it triggered on each change, it needs to be triggered ONLY on unmount with the update values from the props. Is this possible?

React.useEffect(() => {
    return () => {
      if (report.data.draft) { // this is ALWAYS true
        report.snapshot.ref.delete();
      }
    };
  }, []);

How can I conditionally run my code on unmount with the condition being dependant on the updated props state?

Upvotes: 4

Views: 10581

Answers (3)

SS7
SS7

Reputation: 104

Using custom js events you can emulate unmounting a componentWillUnmount even when having dependency. Here is how I did it.

Problem:

    useEffect(() => {
    //Dependent Code
    return () => {
        // Desired to perform action on unmount only 'componentWillUnmount' 
        // But it does not
        if(somethingChanged){
            // Perform an Action only if something changed
        }
    }
},[somethingChanged]);

Solution:

// Rewrite this code  to arrange emulate this behaviour

// Decoupling using events
useEffect( () => {
    return () => {
        // Executed only when component unmounts,
        let e = new Event("componentUnmount");
        document.dispatchEvent(e);
    }
}, []);

useEffect( () => {
    function doOnUnmount(){
        if(somethingChanged){
            // Perform an Action only if something changed
        }
    }

    document.addEventListener("componentUnmount",doOnUnmount);
    return () => {
        // This is done whenever value of somethingChanged changes
        document.removeEventListener("componentUnmount",doOnUnmount);
    }

}, [somethingChanged])

Caveats: useEffects have to be in order, useEffect with no dependency have to be written before, this is to avoid the event being called after its removed.

Upvotes: 2

Nicholas Tower
Nicholas Tower

Reputation: 85002

If you want code to run on unmount only, you need to use the empty dependency array. If you also require data from the closure that may change in between when the component first rendered and when it last rendered, you'll need to use a ref to make that data available when the unmount happens. For example:

const onUnmount = React.useRef();
onUnmount.current = () => {
  if (report.data.draft) {
    report.snapshot.ref.delete();
  }
}
React.useEffect(() => {
  return () => onUnmount.current();
}, []);

If you do this often, you may want to extract it into a custom hook:

export const useUnmount = (fn): => {
  const fnRef = useRef(fn);
  fnRef.current = fn;

  useEffect(() => () => fnRef.current(), []);
};


// used like:
useUnmount(() => {
  if (report.data.draft) {
    report.snapshot.ref.delete();
  }
});

Upvotes: 5

trixn
trixn

Reputation: 16309

The dependency list of your effect is empty which means that react will only create the closure over your outer variables once on mount and the function will only see the values as they have been on mount. To re-create the closure when report.data.draft changes you have to add it to the dependency list:

React.useEffect(() => {
    return () => {
        if (report.data.draft) { // this is ALWAYS true
            report.snapshot.ref.delete();
        }
    };
}, [report.data.draft]);

There also is an eslint plugin that warns you about missing dependencies: https://www.npmjs.com/package/eslint-plugin-react-hooks

Upvotes: 2

Related Questions