componentWillUnmount with React useEffect hook

JavascriptReactjsReact HooksReact Lifecycle

Javascript Problem Overview


How can the useEffect hook (or any other hook for that matter) be used to replicate componentWillUnmount?

In a traditional class component I would do something like this:

class Effect extends React.PureComponent {
    componentDidMount() { console.log("MOUNT", this.props); }
    componentWillUnmount() { console.log("UNMOUNT", this.props); }
    render() { return null; }
}

With the useEffect hook:

function Effect(props) {
  React.useEffect(() => {
    console.log("MOUNT", props);

    return () => console.log("UNMOUNT", props)
  }, []);

  return null;
}

(Full example: https://codesandbox.io/s/2oo7zqzx1n)

This does not work, since the "cleanup" function returned in useEffect captures the props as they were during mount and not state of the props during unmount.

How could I get the latest version of the props in useEffect clean up without running the function body (or cleanup) on every prop change?

A similar question does not address the part of having access to the latest props.

The react docs state:

> If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.

In this case however I depend on the props... but only for the cleanup part...

Javascript Solutions


Solution 1 - Javascript

You can make use of useRef and store the props to be used within a closure such as render useEffect return callback method

function Home(props) {
  const val = React.useRef();
  React.useEffect(
    () => {
      val.current = props;
    },
    [props]
  );
  React.useEffect(() => {
    return () => {
      console.log(props, val.current);
    };
  }, []);
  return <div>Home</div>;
}

DEMO

However a better way is to pass on the second argument to useEffect so that the cleanup and initialisation happens on any change of desired props

React.useEffect(() => {
  return () => {
    console.log(props.current);
  };
}, [props.current]);

Solution 2 - Javascript

useLayoutEffect() is your answer in 2021

useLayoutEffect(() => {
    return () => {
        // Your code here.
    }
}, [])

This is equivalent to ComponentWillUnmount.

99% of the time you want to use useEffect, but if you want to perform any actions before unmounting the DOM then you can use the code I provided.

Solution 3 - Javascript

useLayoutEffect is great for cleaning eventListeners on DOM nodes.

Otherwise, with regular useEffect ref.current will be null on time hook triggered

More on react docs https://reactjs.org/docs/hooks-reference.html#uselayouteffect

  import React, { useLayoutEffect, useRef } from 'react';

  const audioRef = useRef(null);


  useLayoutEffect(() => {
    if (!audioRef.current) return;

    const progressEvent = (e) => {
      setProgress(audioRef.current.currentTime);
    };

    audioRef.current.addEventListener('timeupdate', progressEvent);

    return () => {
      try {
        audioRef.current.removeEventListener('timeupdate', progressEvent);
      } catch (e) {
        console.warn('could not removeEventListener on timeupdate');
      }
    };
  }, [audioRef.current]);



Attach ref to component DOM node

<audio ref={audioRef} />

Solution 4 - Javascript

useEffect(() => {
  if (elements) {
    const cardNumberElement =
      elements.getElement('cardNumber') ||  // check if we already created an element
      elements.create('cardNumber', defaultInputStyles); // create if we did not
            
    cardNumberElement.mount('#numberInput');
  }
}, [elements]);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChrisGView Question on Stackoverflow
Solution 1 - JavascriptShubham KhatriView Answer on Stackoverflow
Solution 2 - JavascriptSimen LView Answer on Stackoverflow
Solution 3 - JavascriptSergey KhmelevskoyView Answer on Stackoverflow
Solution 4 - JavascriptAndrii SvirskyiView Answer on Stackoverflow