{ Component Life Cycle. }

Objectives

By the end of this chapter, you should be able to:

  • Identify life cycle methods related to initialization, changing state and props, and unmounting components
  • Use the componentDidMount life cycle method for AJAX requests
  • Use the componentWillUnmount method to clean up any state the component needs to remove

Introduction

So far we have seen how to create components, render components, and pass down props to child components. React gives us a few special methods that we can use to identify important events in the life cycle of a component. These special methods are called Component Life Cycle methods.

Let's break these events down into a few categories.

Initialization - When a component is rendered for the first time
Changing State - When setState is called
Changing Props - When props change (as a result of state changing)
Removing a Component - When a component is removed (also called unmounting)

Initialization

When a component gets rendered for the first time, we can tap into quite a few life cycle hooks.

constructor

Before the component is mounted, the constructor in the Component is run. Using ES2015 syntax, this is where initial state is set, and where any method binding occurs.

componentWillMount

The first life cycle method we haven't seen before is componentWillMount, which is called before the render method is executed the first time. Since this method is called before render, this means that the component has not been added to the DOM yet and we cannot use React refs. It is important to note that setting the state in this phase will not trigger a re-rendering (since nothing has been rendered!).

componentWillMount(){
    console.log('In componentWillMount');
    // nothing has rendered yet

    // Props have been initialized
    console.log(this.props);

    // State has been initialized
    console.log(this.state);
}

The componentWillMount method is not used as frequently as the next method we will discuss, comonentDidMount. Sometimes you will see it used for initial configuration of the component, but this can also often be done directly in the constructor. In both life cycle methods you have access to props and state, but not much else.

render

Right after componentWillMount is invoked, the render method is invoked and the returned markup is added to the DOM.

componentDidMount

After the component renders, componentDidMount will be invoked. Since the component has rendered, we can access the DOM from this method.

componentDidMount(){
    // the component mounted! Let's do some DOM stuff
}

Very commonly, when a component is rendered, an AJAX call is made to change state in the component. This is done in the componentDidMount.

AJAX Example

We are going to make a component that renders state which was received from an AJAX request. To make our AJAX request, we'll use an AJAX library called axios. You can check out the axios documentation to see all of the library's functionality. We will be using it to make a HTTP GET request.

First, add axios as a dependency to your applicaiton:

npm install --save axios

Next, try out the following code that uses the icanhazdadjoke to get joke data:

import React, { Component } from "react";
import axios from "axios";

class Jokes extends Component {
  constructor(props) {
    super(props);
    this.state = {
      jokes: []
    };
  }

  componentDidMount() {
    axios
      .get(`https://icanhazdadjoke.com/search?term=movie`, {
        headers: { Accept: "application/json" }
      })
      .then(response => {
        const jokes = response.data.results.map(joke => ({
          id: joke.id,
          text: joke.joke
        }));
        this.setState({ jokes });
      });
  }

  render() {
    let data = "Sorry, no joke results yet";
    if (this.state.jokes.length > 0) {
      data = this.state.jokes.map(joke => (
        <div key={joke.id}>
          <p>{joke.text}</p>
        </div>
      ));
    }
    return <div>{data}</div>;
  }
}

export default Jokes;

The above code makes a GET request to https://icanhazdadjoke.com/search?term=movie. The result of the request is all cheesy jokes with the word "movie" in them. Once the response is returned, the callback inside of .then is invoked and we use map to convert the results into an array that contains the joke id and the joke text.

Component Life Cycle Initialization: Recap

To recap initialization, these are the steps that take place when a component is first created and rendered in the DOM:

  1. The constructor method is run and default state and props are set as well as any method binding (using bind to set the correct value of the keyword this)
  2. componentWillMount is invoked
  3. render is invoked
  4. componentDidMount is invoked

Changing State

Whenever we call setState, we can tap into quite a few life cycle hooks:

shouldComponentUpdate

By default, when a parent component updates, all children are re-rendered as well. But shouldComponentUpdate allows us to pass on a re-rendering if we know one is not needed. This function should return a boolean; if it's true, the component will render, and if it's false, the component won't.

shouldComponentUpdate(nextProps, nextState) {
    // return a boolean value
    // true tells react that the component should be updated
    // after a call to setState
    return true;
}

Be very careful with your use of the shouldComponentUpdate life cycle method. This is an advanced optimization that should only be used after you understand React well. Typically React is very good at optimizing changes to the DOM.

componentWillUpdate

componentWillUpdate gets called as soon as the the shouldComponentUpdate returns true. This is sort of analogous to the componentWillMount hook; the difference now is that we're updating an existing component, not mounting a new one. You cannot call setState here. If you need to update state in response to a change in props, use componentWillReceiveProps instead.

componentWillUpdate(nextProps, nextState){
    // perform any preparations for an upcoming update
}

render

We've seen this one before! Just always remember - changing state triggers a re-render unless you modify shouldComponentUpdate.

componentDidUpdate

Finally componentDidUpdate is called after the render method, and is similar to the componentDidMount.

componentDidUpdate(prevProps, prevState){
    // component just updated!
}

As with componentDidMount, componentDidUpdate is a good place to make any AJAX requests for data after an update.

To recap what happens when setState runs:

  1. shouldComponentUpdate runs - if it returns false, render does not run
  2. componentWillUpdate runs
  3. render runs
  4. componentDidUpdate runs

Changing Props

Right when props are being set on a component, we can tap into the same exact hooks as when we change state, except we get one more before shouldComponentUpdate called componentWillReceiveProps.

componentWillReceiveProps

This life cycle method is only called when the props have changed and when this is not an initial rendering. componentWillReceiveProps allows us to update the state depending on the existing and upcoming props before the render method has been called. This is essential when you want to intercept the passing down of props from a parent component to a child component. For example, maybe you want to set some state only when a particular prop is about to change, but not otherwise.

componentWillReceiveProps(nextProps) {
  if (this.props.someProp !== nextProps.someProp) {
    this.setState({
      // set some state
    });
  }
}

After componentWillReceiveProps, the following events occur (similar to before):

  1. shouldComponentUpdate
  2. componentWillUpdate
  3. render
  4. componentDidUpdate

Removing a component (unmounting)

componentWillUnmount

The only method we haven't examined yet is the componentWillUnmount which gets called before the component is removed from the DOM. This method can be beneficial when needing to perform cleanup operations, i.e. removing any timers defined in componentDidMount.

componentWillUnmount(){
    // remove an event listener
    // clear a timeout
    // remove any reference to variables you will not be using to ensure there are no memory leaks
}

Let's look at an example of a component that that has a setInterval that needs to be cleared before the component is unmounted.

import React, { Component } from "react";

class ColoredBox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      backgroundColor: "yellow",
      intervalId: null
    };
  }

  componentDidMount() {
    const intervalId = setInterval(() => {
      this.setState((prevState, props) => {
        const backgroundColor =
          prevState.backgroundColor === "yellow" ? "red" : "yellow";
        console.log(`Changing color to ${backgroundColor}`);
        return { backgroundColor };
      });
    }, 1000);

    this.setState({ intervalId });
  }

  componentWillUnmount() {
    clearInterval(this.state.intervalId);
    console.log(`Interval ${this.state.intervalId} has been cleared`);
  }

  render() {
    const style = {
      width: "100px",
      height: "100px",
      backgroundColor: this.state.backgroundColor
    };
    return <div style={style} />;
  }
}

export default ColoredBox;

You can see in the example that when the component is unmounted, the interval will be cleared in the componentWillUnmount life cycle method. To see the unmount life cycle event being invoked, set up the following BoxContainer component that uses the ColoredBox component:

import React, { Component } from "react";
import ColoredBox from "./ColoredBox";

export default class BoxContainer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      showBox: true
    };
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({ showBox: false });
    }, 7500);
  }

  render() {
    let markup = <div>Nothing to see here</div>;
    if (this.state.showBox) {
      markup = <ColoredBox />;
    }
    return markup;
  }
}

The high level component will show the ColoredBox component until 7500ms have passed. After that time, the showBox state will be set to false, and the ColoredBox component will be unmounted. If you open up the console after loading the page, you can verify by the messages being logged that the timer does indeed eventually get cleared.

Additional Resources

https://facebook.github.io/react/docs/react-component.html

https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/introduction.html

Exercises

Complete the component life cycle exercises.

Head Home

Creative Commons License