Search code examples
javascriptreactjsreact-hooksreact-lifecycle

componentWillUnmount with React useEffect hook


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...


Solution

  • 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]);