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

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 will look at is componentWillMount, which is called before the render method is executed (which means the component has not been added to the DOM yet and we cannot use React refs yet). It is important to note that setting the state in this phase will not trigger a re-rendering.

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.

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 OMDB API to get movie data:

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

export default class Movies extends Component {
  constructor(props) {
    super(props);
    this.state = {
      movies: []
    }
  }

  componentDidMount() {
    axios.get(`http://www.omdbapi.com/?s=star+wars`)
      .then(response => {
        const movies = response.data.Search.map((movie) => {
          return {
            imdbID: movie.imdbID,
            title: movie.Title,
            poster: movie.Poster
          };
        });
        console.log(response.data.Search);
        this.setState({movies});
      });
  }

  render() {
    let data = "Sorry, no movie results yet";
    if (this.state.movies.length > 0) {
      data = this.state.movies.map(m => (
        <div key={m.imdbID}>
          <h4>{m.title}</h4>
          <img src={m.poster} alt={m.title} />
        </div>
      ));
    }
    return (
      <div>
        {data}
      </div>
    );
  }
}

The above code makes a GET request to https://www.omdbapi.com/?s=star+wars. The result of the request is the top 10 movie results that have star wars in the title. 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 movie title and the movie poster.

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!
}

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.

componentWillReceiveProps(nextProps) {
  this.setState({
    // set something
  });
}

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';

export default class ColoredBox extends Component {
  constructor(props) {
    super(props);
    this.intervalId = undefined;
    this.state = {
      backgroundColor: 'yellow',
    }
  }

  componentDidMount() {
    this.intervalId = setInterval(() => {
      this.setState((prevState, props) => {
        const backgroundColor = prevState.backgroundColor === 'yellow' ?
                                'red' : 'yellow';
        return {backgroundColor};
      });
    }, 1000);
  }

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

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

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.intervalId = undefined;
    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.

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