Overview of the Component Lifecycle

The React component lifecycle broadly comprises of two different segments:

  • The initial mount lifecycle (when React attempts to build the component for the first time and inserts it into the DOM)
  • The update lifecycle (after the component has been loaded and rendered for the first time and now is in a state of watching and updating for changes), which loops back on itself over and over

Let's have a look at the various methods that make up each of these component lifecycle stages in the following diagram:

Figure 4.1: Flow of React lifecycle methods

All methods comprising the mount lifecycle are executed first. Then, the program flow proceeds to the update life cycle methods, which are executed repeatedly as long as the component is alive. Let's take a look at the lifecycle itself in this diagram:

In Figure 4.1, we will focus almost entirely on the following pointers:

  • constructor(): The constructor of our component, the first thing that gets called (which you have seen quite a bit in the previous chapters).
  • render(): This is called when the component is rendered to the DOM; again, something you have had already seen.
  • componentDidMount(): This is when the component has been rendered and included in the DOM; this one is new.
  • componentDidUpdate(): This is when the component had a change that triggered an update and was re-rendered to the DOM (for example, if a prop or the state changed); another new one.

We will rarely use the following methods:

  • static getDerivedStateFromProps(props, state)
  • shouldComponentUpdate(nextProps, nextState)
  • getSnapshotBeforeUpdate(prevProps, prevState)

The lifecycle methods in the preceding diagram are specifically executed when the component is alive; that is, included in the browser's DOM and being rendered. When a component unmounts, the componentDidUnmount() method is called. That call happens right before the component is totally removed from the DOM.

As mentioned previously, everything in mount method happens first, and then everything in update method happens repeatedly in a loop. Basically, when a change happens, the loop retriggers, first by getting the new state, including any state that needs to be derived from props, and then it triggers the shouldComponentUpdate method. From there, the rest of the lifecycle fires off sequentially until the program flow encounters the componentDidUpdate method, at which point the React component will essentially pause in the cycle until another update happens and a new event needs to be triggered.

It's important to note that all of this is only a lifecycle in class components. Functional components have no internal functions that they can use since they essentially just render the JSX syntax and have no other structure to them.

Ultimately, you should remember the following basic flow of implementing the lifecycle methods as the following:

MOUNT: (constructor -> render -> componentDidMount) -> UPDATE: (render, componentDidUpdate) -> UPDATE (repeat until component removed) -> UNMOUNT: (componentWillUnmount)

Exercise 4.01: Implementing Lifecycle Methods

In this exercise, we will spend a little time implementing everything that we just learned about how the lifecycle flows. We will avoid the rarely used lifecycle methods and stick to the main lifecycle methods. Since the primary motive of the exercise is to implement lifecycle methods, the exercise in itself is simple, and we will be rendering the lifecycle stage using console.log statements. Let's see how:

  1. Start off by creating a new React project, which we will call lifecycle. Start the project, and take a look at the browser window that should have opened for you automatically:

    $ npx create-react-app lifecycle

    $ cd lifecycle

    $ yarn start

  2. Delete src/logo.svg and delete the contents of src/App.css.
  3. In order to disable strict mode, change the render part in src/index.js as follows:

    ReactDOM.render(<App />, document.getElementById('root'));

  4. Clean out the contents of the App component and replace them with a class component instead. Since we are using a class component here, we will need to add the following code:

    import React, { Component } from 'react';

    import "./App.css";

    class App extends Component {

      render() {

      return (

        <p className="App">Hello Lifecycle</p>

      )

      }

    }

    export default App;

    This should give us an initial component that looks like the following:

    Figure 4.2: Initial component

  5. Add our first lifecycle console log statement by adding console.log() to a constructor (you will need to add the constructor, too):

    constructor(props) {

    super(props);

      console.log("Constructor");

    }

    When your browser reloads, if you open up the JavaScript console, you should see the Constructor statement appear in your log:

    Figure 4.3: Console log of the component

  6. Add a componentDidMount() function to the component. In that, you are going to add a console.log() call that outputs Component Did Mount to the log:

    componentDidMount() {

    console.log("Component Did Mount");

    }

  7. Add a console.log() call to your render() function before you return the JSX. The output will just be Render. Remember that render() is earlier in the component lifecycle than componentDidMount(), so this log statement will actually happen before the one we wrote earlier:

    Figure 4.4: Console to show the messages for the lifecycle methods

    After steps 1 through 5, we have implemented all of the mount lifecycle methods, so now we can move into the update loop lifecycle methods. To do this, we will need to set up a particular state that will allow us to watch for updates appropriately.

  8. First, go into the constructor. Add a new state here with a state property called cycle that will start with a value of 0.
  9. Add a setInterval() statement that will update the component's cycle by 1 every second (1000 milliseconds):

    constructor(props) {

      super(props);

      console.log("Constructor");

      this.state = { cycle: 0 };

      setInterval(

        () => this.setState({ cycle: this.state.cycle + 1 }),

        1000

      );

    }

    While this is technically enough now to see the updates in the log, we are going to quickly add something to the render function that will tell us the current value of the cycle state.

  10. Update the render function to show the current value of the cycle state:

    render() {

      console.log("Render");

      return (

          <p className="App">Hello Lifecycle: Cycle {this.state.cycle}</p>

      );

    }

  11. Add the componentDidUpdate() method to your class component that will print Component Did Update in the console

    With all of this done, we should see each of the update cycle methods looping in our console after the initial mount cycle methods:

    componentDidUpdate() {

      console.log("Component Did Update");

    }

    The output is as follows:

Figure 4.5: Console to show the output

As you can see, the console.log statement shows the output as expected. You can see just how the initial mount lifecycle leads into the looping update lifecycle. Now, let's explore each lifecycle method inpidually to give us a greater insight into what each one is responsible for.