DEV Community

Yohanes Setiawan
Yohanes Setiawan

Posted on

Lifecycle in React Component

Everything in React is made up of components or parts of components and every components follow a certain lifecycle, almost like the lifecycle of any living thing on earth 🌎. They are born, grow, and eventually die.

The phase when they are born is called mount. When they are grow is called update. The last phase of death is called unmount.

This whole process is called the Component Lifecycle. For each of these phases, React renders certain built-in methods called lifecycle methods that control the behavior of components.

We can see on the diagram below all of React lifecycle methods associated with the mounting, updating, umounting of the component. (diagram credit: dan abramov)

react lifecycle diagram

I will explain in following section about each methods that available for each lifecycle in more detail.

Mounting Lifecycle Methods

The mounting phase, is a phase where is the component is created and inserted into the DOM.

1. constructor()

constructor() is the very first method called as the component is created. This method is used for two purposes:

  • To initialize the local state of a component

  • To bind an event handling method to an instance

Here is an example of constructor() method in action:

constructor(props) {
  super(props);
  this.state = { fruit: 'Apple 🍎' }
  this.eatFruit = this.eatFruit.bind(this);
}
Enter fullscreen mode Exit fullscreen mode

Note that, constructor() is the first method invoked before the component is mounted into the DOM. We should not introduce any side effect in this method.

2.getDerivedFromProps()

getDerivedStateFromProps() is a new React lifecycle method as of React 17 and designed to replace componentWillReceiveProps().

The purpose of this function is to make sure that the state and props are in sync for when it is required.

getDerivedStateFromProps() lifecycle runs after the constructor method and before the componentDidMount() lifecycle run. This function accepts two paramenters props and state. We have to return an object to update state or null to indicate that nothing has changed.

To get better understanding of how getDerivedStateFromProps() works, let see the following code:

import React from 'react';

class FavFruit extends React.Component {
  constructor() {
    super(props);
    this.state = {
      favFruit: 'Banana 🍌';
    }
  }

  render() {
    return(
      <h2>My Favorite Fruit is {this.state.favFruit}</h2>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

When the component mount, we will see My Favorite Fruit is Banana 🍌 is showed up in the browser. How can we change our state from Banana 🍌 into Apple 🍎 before render()? Actually, we can do it via getDerivedStateFromProps().

import React from 'react';

class FavFruit extends React.Component {
  constructor() {
    super(props);
    this.state = {
      favFruit: 'Banana 🍌';
    }
  }

  // Not Recommended. For explaining purpose only.
  static getDerivedStateFromProps(props, state) {
    return {
      favFruit: 'Apple 🍎'
    }
  }

  render() {
    return(
      <h2>My Favorite Fruit is {this.state.favFruit}</h2>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

When the component mount, we will see My Favorite Fruit is Apple 🍎 is showed up in the browser rather than Banana 🍌. How can this works? By returning an object, getDerivedStateFromProps() can utilize its data and doing update for favFruit before render() method is called to render the DOM. Noted that getDerivedStateFromProps() is takes 2 argument, the first argument is props and the second argument is state.

This short example is contrived and not really representative of the way how to use getDerivedStateFromProps(). But it’s helpful for understanding the basics. However, just because we can update state via getDerivedStateFromProps() it doesn’t mean we should. There are specific use cases on when we should use getDerivedStateFromProps(). If we use it in the wrong context, we’ll be solving a problem with the wrong tool πŸ”¨.

When should we use the getDerivedStateFromProps()? We must know why this method is created in first place. There are some cases where the component is needed update the internal state in response to a prop change. Component state in this manner is referred to as derived state.

3.render()

render() method is called after getDerivedStateFromProps() is called.

import React from 'react';

class HelloWorld extends React.Component {
  render() {
    return <h1>Hello World! 🌎</h1>
  }
}
Enter fullscreen mode Exit fullscreen mode

What we return in render() will be rendered into the DOM. In the example above we are returning JSX. But we can also return an array of JSX string, number, or if we don’t want to render anything, we could return a boolean or null

import React from 'react';

class HelloWorld extends React.Component {
  render() {
    return [
      <div key='1'>"Hello World! 🌎"</div>,
      <div key='2'>"Hello World! 🌎"</div>]
  }
}
Enter fullscreen mode Exit fullscreen mode

Note that in example above we add key property in jsx. It is used by React to identify and keep track for which items in the list are changed, updated, or deleted.

4.componentDidMount()

After render() is called (The component is mounted into the DOM), componentDidMount() will be invoked. This method is the place when you should do a side effect thing. Like make a subscription to an API, data fetching, or maybe make a change into the DOM.

import React from 'react';

class ChangeDocTitle extends React.Component() {
  componentDidMount() {
    document.title = 'Hello World 🌎';
  }

  render() {
    return<h1>This component will change the title of your current tab</h1>
  }
}

Enter fullscreen mode Exit fullscreen mode

Updating Lifecycle Methods

The update phase, is a phase where the component doing a re-render (updating the state) that has been triggered because of state or prop change.

1.getDerivedFromProps()

This method is also invoked when the component doing an update. Since I already give an explanation of getDerivedFromProps() on mounting phase, please refer on that πŸ€“. Note that getDerivedFromProps() is invoked when the component is mounted and when the component is doing re-render.

2.shouldComponentUpdate()

After getDerivedProps() is called, shouldComponentUpdate() is invoked. This method accepts two arguments, the first argument is the nextProps and the second argument is nextState.

The purpose of this function is to determine is wheter the component is will be re-render by returning true or not by returing false.

import React from 'react';

class FavFood extends Component {
  constructor(props) {
    super(props);
    this.state = {
      favMeal: 'French Fries 🍟'
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    // let's assume that the currentProps in this example { favDrink: 'Cola πŸ₯€' }
    if (nextProps.favDrink == 'Cola πŸ₯€') {
      // The component is won't be updated if the currentProps.favDrink is still same with the nextProps.favDrink even when the nextState is different with currentState
      return false;
    } else {
      return true;
    }
  }

  render() {
    return (
      <div>
        <h1>My Fav Drink is: {this.props.favDrink}</h1>
        <h1>My Fav Meal is: {this.state.favMeal}</h1>
        <button onClick={() => {this.setState({favMeal: 'Burger πŸ”'})}}>Change My Meal! 🍽</button>
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Notice that in the contrived example above, we can click Change My Meal! 🍽 to change the state of favMeal however in shouldComponentUpdate() we make a conditioning where if the nextProps of favDrink is still Cola πŸ₯€ (still the same with currentProps) then the component will be not be updated.

shouldComponentUpdate() is a powerful method. However as proverb says "With Great Power Comes Great Responsibility", we must treat this method with caution. If we didn't careful with our conditioning and accidentally returning false, the component is not updated and this can be a problem and it will be hard to debug it 🐞.

3.render()

render() method is called immediately depending on the returned value from shouldComponentUpdate(), which defaults to true.

4. getSnapshotBeforeUpdate()

Once render() is called, getSnapshotBeforeUpdate() is invoked just before the DOM is being rendered. It is used to store the previous values of the state after the DOM is updated. Any value returned by getSnapshotBeforeUpdate() will be used as a parameter for componentDidUpdate() which will be explained after this. getSnapshotBeforeUpdate() accepts two arguments which is prevProps and prevState.

import React from 'react';

class FavSuperHero extends React.Component {
  constructor(props) {
    super(props);
    this.state = { mySuperHero: 'Thor ⚑️' }
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({ mySuperHero: 'IronMan πŸš€' })
    }, 1000)
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    document.getElementById('prevHero').innerHTML = `Previous Super Hero: ${prevState.mySuperHero}`
  }

  componentDidUpdate() {
    document.getElementById('newHero').innerHTML = `New Super Hero: ${prevState.mySuperHero}`
  }

  render() {
    return(
      <div>
        <h1 id='prevHero'></h1>
        <h1 id='newHero'></h1>
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

From the code above we can get the prevState and showing Previous Super Hero: Thor ⚑️ which is the old state that have been updated into New Super Hero: IronMan πŸš€ which is the current state.

Note that, it is highly recommended to never directly to set state in getSnapshotBeforeUpdate(), otherwise it will trigger render() method.

5. componentDidUpdate()

componentDidUpdate() is invoked as soon as the render() method called (update happens). The common use case for the componentDidUpdate() method is to updating the DOM in response to prop or state changes. This method accept three arguments, the first is prevProps, second is prevState, and the third argument is the value that has returned from getSnapshotBeforeUpdate() method.

We can also call setState() within this method. However, please be careful incorrect usage of setState within this componentDidUpdate() can cause an infinite loop. Note that, you will need to wrap setState() in a condition to check for state or prop changes from previous one.

componentDidUpdate(prevProps) {
  if (this.props.accessToken !== null && prevProps.accessToken !== null) {
    this.getUser(accessToken)
    .then(user => {
      if (user) {
        this.setState(user);
      }
    })
    .catch(e => console.log('Error fetching user data'))
  }
}
Enter fullscreen mode Exit fullscreen mode

In the example above, we do a condition where if the accessToken is not null, we can fetch the user data and then updating our user state. If we are does not have access token, componentDidUpdate() will not call getUser() method, hence preventing to set the user state.

Unmounting lifecycle method

The unmounting phase, is a phase where the component will be unmounted (destroyed) from the DOM.

1. componentWillUnmount()

This method will be called when the component is unmounted (destroyed) from DOM πŸ’£. This is the place where you perform for any cleanup method, cancel the network request or purge the unwanted subscriptions that was created in the componentDidMount() method.

import React from 'react';

class Building extends React.Component {
  componentWillUnmount() {
    console.log('The building is destroyed πŸ’₯');
  }

  render() {
    return <h1>My Building 🏒</h1>;
  }
}

class DestroyBuilding extends React.Component {
  constructor(props) {
    super(props);
    state = {
      showBuilding: true
    };
  }

  render() {
    let building;
    if (this.state.showBuilding) {
      building = <Building />
    };

    return(
      <div>
        {building}
        <button onClick={() => this.setState(showBuilding: false)}>Detonate Building πŸ’£</button>
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

When you click Detonate Building πŸ’£ button, 'The building is destroyed πŸ’₯' text will be logged into our console.

Top comments (0)