This post is part of the series 30 Days of React.

In this series, we're starting from the very basics and walk through everything you need to know to get started with React. If you've ever wanted to learn React, this is the place to start!

Lifecycle Hooks

Edit this page on Github

Today, we'll look through a few of the most common lifecycle hooks we can use with React components and we'll discuss why they are useful and when we should each one.

Congrats! We've made it to the end of the first week on React and we've already covered so much ground. We just finished working with stateful components to keep track of a component's internal state. Today, we're going to pause on implementation and talk a bit about how a component lives in an application. That is, we'll talk about the component's lifecycle.

As React mounts our application, it gives us some hooks where we can insert our own functionality at different times in the component's lifecycle. In order to hook into the lifecycle, we'll need to define functions on our component which React calls at the appropriate time for each hook. Let's dive into the first lifecycle hook:

componentWillMount() / componentDidMount()

When a component is defined on a page in our application, we can't depend upon it being available in the DOM immediately as we're defining virtual nodes. Instead, we have to wait until the component itself has actually mounted in the browser. For functionality that we need to run when it has been mounted, we get two different hooks (or functions) we can define. One that is called just before the component is due to be mounted on the page and one that is called just after the component has been mounted.

What does mounting mean?

Since we're defining virtual representations of nodes in our DOM tree with React, we're not actually defining DOM nodes. Instead, we're building up an in-memory view that React maintains and manages for us. When we talk about mounting, we're talking about the process of converting the virtual components into actual DOM elements that are placed in the DOM by React.

This is useful for things such as fetching data to populate the component. For instance, let's say that we want to use our activity tracker to display github events, for example. We will want to load these events only when the data itself is going to be rendered.

Recall we defined our Content component in our activity list:


class Content extends React.Component {
  render() {
    const {activities} = this.props; // ES6 destructuring
    
    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        {activities.map((activity) => (
          <ActivityItem
            activity={activity} />
        ))}
        
      </div>
    )
  }
}

Let's update the Content component to make a request to the github.com events api and use the response to display the activities. As such, we'll need to update the state of the object.

As we did yesterday, let's update our component to be stateful by setting this.state to an object in the constructor

class Content extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      activities: []
    }
  }

  // ...
}

Now, we'll want to make an HTTP request when the component itself is getting ready to be mounted (or just after it mounts). By defining the function componentWillMount() (or componentDidMount()) in our component, React runs the method just before it mounts in the DOM. This is a perfect spot for us to add a GET request.

Let's update the Content component with the request to the github api. Since we'll only want to display a small list, let's take the latest four events.

We've stored a static JSON file of github data that we'll load directly from source here (we'll get back to making AJAX requests in a few days) using promises. For now, let's focus on how we'll implement updating our component with new data:

class Content extends React.Component {
  // ...
  componentWillMount() {
    this.setState({activities: data});
  }
  // ...
}

Notice that we didn't change anything else from our Content component and it just works.

componentWillUpdate() / componentDidUpdate()

Sometimes we'll want to update some data of our component before or after we change the actual rendering. For instance, let's say we want to call a function to set up the rendering or call a function set when a component's props are changed. The componentWillUpdate() method is a reasonable hook to handle preparing our component for a change (as long as we don't call this.setState() to handle it as it will cause an infinite loop).

Since we won't really need to handle this in-depth, we won't worry about setting up an example here, but it's good to know it exists. A more common lifecycle hook we'll use is the componentWillReceiveProps() hook.

componentWillReceiveProps()

React will call a method when the component is about to receive new props. This is the first method that will be called when a component is going to receive a new set of props. Defining this method is a good time to look for updates to specific props as it gives us an opportunity to calculate changes and update our component's internal state.

This is the time when we can update our state based on new props.

One thing to keep in mind here is that even though the componentWillReceiveProps() method gets called, the value of the props may not have changed. It's always a good idea to check for changes in the prop values.

For instance, let's add a refresh button to our activity list so our users can request a rerequest of the github events api.

We'll use the componentWillReceiveProps() hook to ask the component to reload it's data. As our component is stateful, we'll want to refresh this state with new data, so we can't simply update the props in a component. We can use the componentWillReceiveProps() method to tell the component we want a refresh.

For instance, let's add a button on our containing element that passes a requestRefresh boolean prop to tell the Content component to refresh.


class Container extends React.Component {
  constructor(props) {
    super(props);

    this.state = {refreshing: false}
  }

  // Bound to the refresh button
  refresh() {
    this.setState({refreshing: true})
  }

  // Callback from the `Content` component
  onComponentRefresh() {
    this.setState({refreshing: false});
  }

  render() {
    const {refreshing} = this.state;
    return (
      <Panel>
        <Header title="Github activity" />
        {/* refreshing is the component's state */}
        <Content
          onComponentRefresh={this.onComponentRefresh.bind(this)}
          requestRefresh={refreshing}
          fetchData={fetchEvents} />
        {/* A container for styling */}
        <Footer>
          <button onClick={this.refresh.bind(this)}>
            <i className="fa fa-refresh" />
            Refresh
          </button>
        </Footer>
      </Panel>
    )
  }
}

Using this new prop (the requestRefresh prop), we can update the activities from our state object when it changes value.


class Content extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false, // <~ set loading to false
      activities: []
    }
  }

  // Update the data when the component mounts
  componentDidMount() {
    this.updateData();
  }

  componentWillReceiveProps(nextProps) {
    // Check to see if the requestRefresh prop has changed
    if (nextProps.requestRefresh !== this.props.requestRefresh) {
      this.setState({loading: true}, this.updateData);
    }
  }

  // Call out to github data and refresh directory
  updateData() {
    this.setState({
      loading: false,
      activities: data
    }, this.props.onComponentRefresh);
  }

  render() {
    const {loading, activities} = this.state;
    
    return (
      <div className="content">
        <div className="line"></div>
        {/* Show loading message if loading */}
        {loading && <div>Loading</div>}
        {/* Timeline item */}
        {activities.map((activity) => (
          <ActivityItem
            activity={activity} />
        ))}
        
      </div>
    )
  }
}

componentWillUnmount()

Before the component is unmounted, React will call out to the componentWillUnmount() callback. This is the time to handle any clean-up events we might need, such as clearing timeouts, clearing data, disconnecting websockets, etc.

For instance, with our clock component we worked on last time, we set a timeout to be called every second. When the component is ready to unmount, we want to make sure we clear this timeout so our JavaScript doesn't continue running a timeout for components that don't actually exist.

Recall that our timer component we built looks like this:


class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }

  componentDidMount() {
    this.setTimer();
  }

  setTimer() {
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }

  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }

  getTime() {
    const currentTime = new Date();
    return {
      hours: currentTime.getHours(),
      minutes: currentTime.getMinutes(),
      seconds: currentTime.getSeconds(),
      ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
    }
  }

  // ...
}

When our clock is going to be unmounted, we'll want to clear the timeout we create in the setTimer() function on the component. Adding the componentWillUnmount() function takes care of this necessary cleanup.

class Clock extends React.Component {
  // ...
  componentWillUnmount() {
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
  }
  // ...
}

These are a few of the lifecycle hooks we can interact with in the React framework. We'll be using these a lot as we build our react apps, so it's a good idea to be familiar with them, that they exist, and how to hook into the life of a component.

We did introduce one new concept in this post which we glossed over: we added a callback on a component to be called from the child to it's parent component. In the next section, we're going to look at how to define and document the prop API of a component for usage when sharing a component across teams and an application in general.


Ari Lerner

Hi, I'm Ari. I'm the author of ng-book and I've been teaching Web Development for a long time. I like to speak at conferences and eat spicy food. I technically got paid while I traveled the country as a professional comedian, but have come to terms with the fact that I am not funny.

Connect with Ari on Twitter at @auser.