Component Lifecycles

Posted by Lisa Feng on March 7, 2021

Last week I talked about the state hook (useState) for React’s functional components. This week I’ll be diving into the effect hook: useEffect.

Class vs. Functional

Traditionally, only class components were able to take advantage of methods designated to the three phases of a component’s lifecycle: mounting, updating, and unmounting (the corresponding methods are: componentDidMount, componentDidUpdate, and componentWillUnmount). Now, we have useEffect for functional components, which essentially combines all three of those methods into one hook.

Mounting and Updating

A class component with a count key in its state object would utilize the mounting and updating lifecycle methods like this:

componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

Because we frequently want the same thing to happen whether the component has just been mounted or updated, there is identical code in both these methods.

The equivalent effect in a functional component would look like this:

useEffect(() => {
    document.title = `You clicked ${count} times`;
  })

Because useEffect runs after every render and update, there’s no need to separate the “mount” and “update”. Effects will simply happen “after render”.

Unmounting

A class component with an isOnline key in its state object would utilize the mounting and unmounting lifecycle methods like this:

componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }

You’ll notice that componentDidMount() and componentWillUnmount() have duplicate code (once again). Even though they are both related to the same effect, lifecycle methods have to split this into two different methods.

The equivalent effect in a functional component would look like this:

useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Cleaning up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  })

Every effect has the option to return a function that cleans up after it (the “unmounting” counterpart).

More Thoughts

Effects are scheduled everytime we re-render, replacing the previous effect. We can think of each effect being tied to a particular render. Apps built with useEffect will feel more responsive since it doesn’t block the browser from updating the screen, like componentDidMount and componentDidUpdate will do.

I’ll admit, after looking more into these React hooks, I am eager for my next React app to utilize the full power of functional components.