React "hooking" into lifecyle methods

Posted on February 08, 2019


The useEffect hook is the combination of componentDidMount, componentDidUpdate and componentWillUnmount class lifecycle methods. This hook is the ideal place to setup listener, fetching data from API and removing listeners before component is removed from the DOM.

useEffect function is like saying, "Hi React, please do this thing after you render. ALWAYS."

Let's look at an example of useEffect in comparison with class lifecycle methods. Normally in class component, we write this kind of code:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      role: 'web developer',
      name: 'Nathan',
    };
  }

  componentDidMount() {
    console.log(
      `didMount: Hello I'm ${this.state.name} and I'm a ${this.state.role}`
    );
  }

  componentDidUpdate() {
    console.log(
      `didUpdate: Hello I'm ${this.state.name} and I'm a ${this.state.role}`
    );
  }

  render() {
    return (
      <div>
        <p>{`Hello I'm ${this.state.name} and I'm a ${this.state.role}`}</p>
        <button
          onClick={() =>
            this.setState({ name: 'Gary', role: 'data scientist' })
          }
        >
          Change me
        </button>
      </div>
    );
  }
}

Since componentDidMount is run only once when the component is inserted into the DOM tree structure, subsequent render won't trigger the method anymore. In order to do run something on each render, you need to use componentDidUpdate method.

Using useEffect hook is like having both componentDidMount and componentDidUpdate in one single method, since useEffect runs on every render. It accepts two arguments:

  1. (mandatory) A function to run on every render
  2. (optional) An array of state variables to watch for changes. useEffect will be skipped if none of the variables are updated.

Rewriting the above class into function component would look like this:

const Example = props => {
  const [name, setName] = useState('Nathan');
  const [role, setRole] = useState('web developer');

  useEffect(() => {
    console.log(`Hello I'm ${name} and I'm a ${role}`);
  });

  return (
    <div>
      <p>{`Hello I'm ${name} and I'm a ${role}`}</p>
      <button
        onClick={() => {
          setName('Gary');
          setRole('data scientist')
          }}>
        Change me
      </button>
    </div>
  )
}

Note: If you're confused with the useState hook above, please refer to my useState introduction

The function component we just write will run the function inside of useEffect function on each render. Now this isn't optimal because the state won't be updated after the first click. This is where useEffect second argument come into play.

useEffect(() => {
    console.log(`Hello I'm ${name} and I'm a ${role}`);
  }, [name, role] );

By adding the array above, React will skip running the console.log method when there is no change to the state variables.

The componentWillUnmount and skipping componentDidUpdate part

You might have some code that need to run when the component will be removed from the DOM tree. In useEffect hook, you can specify a componentWillUnmount method by returning a function from the first argument. Here is an example:

useEffect(() => {
    console.log(`Hello I'm ${name} and I'm a ${role}`);

    return () => { console.log("componentWillUnmount"); }
  }, [name, role] );

Since componentWillUnmount is used for cleaning whatever left behind by your component, it's really hard to give some practical example. But one example might be when using third party library like C3.js from Ashley Wilson

We can rewrite his code from this:

componentDidMount () {
  this._initGraph();
}

componentWillUnmount () {
  this.graph = this.graph.destroy();
}

into this:

useEffect(() => {
    this._initGraph();

    return () => { this.graph = this.graph.destroy(); }
  }, [] );

Do you wonder why we pass an empty array in the second argument? That's because we want the hook to run this._initGraph() only once on didMount. You can pass an empty array [] to tell React that this component never re-render.

Conclusion

useEffect is the function component way to create lifecycle methods, and in the spirit of React hooks, it does make React code cleaner and use fewer lines of code.

Share this:
LinkedIn
Reddit
WhatsApp

Get Free Guides