DEV Community

Cover image for The Lifecycle of React Components
Tifani Dermendzhieva for Zone 2 technologies Ltd.

Posted on • Originally published at zone2.tech

The Lifecycle of React Components

A React component undergoes three distinct phases in its lifecycle (i.e. mounting, updating, and unmounting). Each phase has specific methods responsible for a particular stage in the component's lifecycle.

Be aware that these lifecycle methods are specific to class-based components and are, therefore,
not applicable to functional components. When working with functional components, however, the state is used in a similar manner, where the data is stored and manipulated with abstracted versions of the lifecycle methods called "hooks".

Here you'll learn more about the React component lifecycle and the different methods within each phase, as well as, how to use the most common hooks useState and useEffect.

The Lifecycle of a React Component

Phase 1: Mounting

The first phase in a component's lifecycle is called "Mounting".
The mounting phase begins with the creation of a new component and its insertion into the DOM.

It can only happen once and React has four built-in methods that get called in this exact order:

  • constructor(props) :

    • called before anything else;
    • called with props as an argument;
    • sets the initial state;
    • must always start with the super(props) in order to inherit methods from its parent (i.e. React.Component);
  • getDerivedStateFromProps(props, state) :

    • called right before rendering the elements in the DOM;
    • sets the state object based on the props;
    • accepts two arguments: props and state;
    • returns an object with the changes to the state, or null if there is no change.
  • render() :

    • called after the props and state have been set;
    • inserts the HTML into the DOM;
    • returns the JSX which will be rendered;
    • supposed to be pure (i.e. must NOT modify the state, have any direct interaction with the browser, or any other kind of side effect)
  • componentDidMount() :

    • called after the component is rendered;
    • runs statements that require that the component be in the DOM;
    • can include side effects like sending network requests and updating the component's state;
    • caution: it may accidentally cause unnecessary re-renders.

Note: The render() method is required and will always be called, the rest are optional and will only be called if defined.

Here is an example of the construtor and render methods:

First, the super(props) is called, in order to initialise the React.Component's constructor and inherit its methods.

Then, inside the constructor method the variable colour is set to "purple".

And finally, the render method is used to display an <h1> element, containing the text "My favourite colour is purple"

class FavouriteColour extends React.Component {
  constructor(props) {
    super(props);
    this.state = { colour: "purple" };
  }
  render() {
    return <h1>My favorite colour is {this.state.colour}</h1>;
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour />);
Enter fullscreen mode Exit fullscreen mode

Now, let's implement the getDerivedStateFromProps(props, state) method.

Initially the colour is set to "purple", but then the getDerivedStateFromProps method
updates the colour, depending on the favouriteColour attribute, which, in this case, is "yellow".

The resulting text, rendered on the page, is "My favourite colour is yellow".

class FavouriteColour extends React.Component {
  constructor(props) {
    super(props);
    this.state = { colour: "purple" };
  }
  static getDerivedStateFromProps(props, state) {
    return { colour: props.favouriteColour };
  }
  render() {
    return <h1>My favorite colour is {this.state.colour}</h1>;
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

Suppose that the colour should not be changed right away. Instead, the initial value has to be displayed first and then, after some time has passed, it should be updated to "yellow".

Well, in that case, the componentDidMount method could be used to change the colour to yellow 10 seconds after the component has been rendered.

class FavouriteColour extends React.Component {
  constructor(props) {
    super(props);
    this.state = { colour: "purple" };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({ colour: "yellow" });
    }, 10000);
  }
  render() {
    return <h1>My favorite colour is {this.state.colour}</h1>;
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

Phase 2: Updating

The second phase is the updating phase and it can occur multiple times.

It is triggered when there is a change in the component's state or props and causes the component to re-render with the updated values of the state/props.

The updating phase includes the following methods, in this exact order:

  • getDerivedStateFromProps() : like in the mounting phase, it sets the state based on the initial props

  • shouldComponentUpdate() :

    • can accept nextProps and nextState as arguments, however, they are optional;
    • returns a Boolean value that specifies whether React should continue with the re-rendering or not;
    • the default value is true;
    • ignored when forceUpdate() is invoked;
    • specifically intended for performance optimisation
  • render() : re-renders the HTML in the DOM, with the updated values;

  • getSnapshotBeforeUpdate() :

    • provides access to the props and state before the update, so that even after the update, you can still check what the values had been before it;
    • should also include the componentDidUpdate method, otherwise you will get an error;
    • use case: handling scroll positions in a chat app. Upon receiveing new messages, the app shouldn’t push the currently visible ones out of view.
  • componentDidUpdate() :

    • called after the component is updated in the DOM;
    • required if getSnapshotBeforeUpdate is called;
    • accepts up to three parameters: prevProps, prevState, and snapshot;
    • allows you to create side effects such as sending network requests or calling this.setState;
    • caution: avoid the use of setState in this method, or it will result in an infinite loop of re-rendering.

Let's add a button that changes the colour to "green".

class FavouriteColour extends React.Component {
  constructor(props) {
    super(props);
    this.state = { colour: "purple" };
  }
  static getDerivedStateFromProps(props, state) {
    return { colour: props.favouriteColour };
  }
  changeColour = () => {
    this.setState({ colour: "green" });
  };
  render() {
    return (
      <div>
        <h1>My favorite colour is {this.state.colour}</h1>
        <button type="button" onClick={this.changeColour}>
          GREEN
        </button>
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

Once the button is clicked the state.colour is set to "green". However, the change in state triggers the components's updating phase, which in turn triggers the getDerivedStateFromProps. Once it has been called, it updates the state colour to "yellow" (the value of the favouriteColour attribute). As a result, the colour displayed on the page is not "green", but "yellow".

To disable this behaviour the getDerivedStateFromProps method must be removed.

class FavouriteColour extends React.Component {
  constructor(props) {
    super(props);
    this.state = { colour: "purple" };
  }

  changeColour = () => {
    this.setState({ colour: "green" });
  };
  render() {
    return (
      <div>
        <h1>My favorite colour is {this.state.colour}</h1>
        <button type="button" onClick={this.changeColour}>
          GREEN
        </button>
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour />);
Enter fullscreen mode Exit fullscreen mode

Now, let's add the getSnapshotBeforeUpdate method and display what the colour had been set to before the update.

To do so, two <div> elements are added. One <div> will contain the value of the colour before and the other - after the re-rendering.

In the getSnapshotBeforeUpdate method, the previous state is accepted as argument, then the <div> with id before-update is selected and its text content set the colour to be displayed.

Whenever the getSnapshotBeforeUpdate is used, the componentDidUpdate method must be included as well. In the example, it is used to set the text content of the second <div> to the value of colour after the update.

class FavouriteColour extends React.Component {
  constructor(props) {
    super(props);
    this.state = { colour: "purple" };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({ colour: "yellow" });
    }, 10000);
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    document.getElementById("before-update").textContent =
      "Before: " + prevState.colour;
  }
  componentDidUpdate() {
    document.getElementById("after-update").textContent =
      "After: " + this.state.colour;
  }
  render() {
    return (
      <div>
        <h1>My favorite colour is {this.state.colour}</h1>
        <div id="before-update"></div>
        <div id="after-update"></div>
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

Phase 3: Unmounting

The final phase in the lifecycle of a React component is unmounting. It occurs right before the component is removed from the DOM.

React has only one built-in method that is called when a component is unmounted:

  • componentWillUnmount() :
    • called when the component is about to be removed from the DOM;
    • meant for any necessary clean up of the component( i.e. canceling any network requests);
    • once this method has been executed, the component is destroyed.

useState and useEffect React Hooks

Before the release of React 16.8 there were two kinds of components in terms of state:
the class-based stateful component, and the stateless functional components.

In version 16.8, however, the so-called hooks were introduced. They are functions that allow you to "hook into" the React state and the lifecycle of the components.

As a result, developers can now access and modify the state from functional components, which would have, otherwise, required a class. With this change, building components has become easier and less verbose.

React has released several default hooks which are ready to use, however, you can also create your own custom hooks.

Bear in mind that there is a naming convention for custom hooks. The name of the hook must begin with use (i.e. useCustomHook, useColour, useTheme etc.)

Among the default hooks, the most commonly used ones are useState and useEffect:

  • useState(initialState):

    • used to store variables in the state;
    • accepts one argument: initialState, which will be set as the initial value of the state;
    • returns two values: the state value, and a function to be used to update the state;
    • the update function's name usualy begins with set and it accepts one argument, newState, which replaces the existing state.

Generic example of useState:

const [value, setValue] = useState(initialState);
Enter fullscreen mode Exit fullscreen mode

Let's rewrite one of the examples above as a functional component and use useState to update the colour:

function FavouriteColour(props) {
  const [colour, setColour] = React.useState(props.favouriteColour);

  const changeColour = () => {
    setColour("green");
  };

  return (
    <div>
      <h1>My favorite colour is {colour}</h1>
      <button type="button" onClick={changeColour}>
        GREEN
      </button>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

In this example the text displayed initially on the page is "My favourite colour is yellow" because the initial value of useState is set to "yellow".

Similarly to the previous examples, "yellow" comes from the favouriteColour attribute, which is received by the component as a prop. Then, upon clicking the GREEN button, the page is re-rendered and the updated text reads "My favourite colour is green".

  • useEffect(function, dependencyArray):

    • allows you to add side effects (such as send network requests), which aren’t allowed within the function's main body;
    • allows you to call functions upon updates on the state;
    • accepts a function as an argument, inside which you write all the side effects;
    • accepts an array as second argument called the dependency array (optional);
    • invoked after every browser paint and before any renders (depends on the dependency array);
    • can return another function called the clean-up function, which is used to clean up the side effects when the component is destroyed.

The dependency array is an array of variables, which will trigger the useEffect function when their value changes.

If the dependency array is empty, then the useEffect runs only once, before render.

Alternatively, if it is not defined, useEffect will run once during mounting and then again on every re-render.

function FavouriteColour(props) {
  const [colour, setColour] = React.useState(props.favouriteColour);
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    setCount((previousCount) => (previousCount += 1));
  }, [colour]);

  const changeColour = (newColour) => {
    setColour(newColour);
  };

  return (
    <div>
      <h1> My favourite colour has been updated {count} times.</h1>
      <h1>My favorite colour is {colour}</h1>

      <button type="button" onClick={() => changeColour("green")}>
        GREEN
      </button>
      <button type="button" onClick={() => changeColour("yellow")}>
        YELLOW
      </button>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

In the example above useEffect is used to increment the state of count each time the state of colour is updated.

Note that when the page is rendered for the first time the count has already been incremented once. This happens because the function in useEffect is executed every time the value of colour is modified, inclding the inital one.

If this is not the desired outcome, instead of using useEffect, the setCount could be called inside the changeHandler to update the count.
Now, the initial render does not affect the initial value of count.

function FavouriteColour(props) {
  const [colour, setColour] = React.useState(props.favouriteColour);
  const [count, setCount] = React.useState(0);

  const changeColour = (newColour) => {
    setColour(newColour);
    setCount((previousCount) => (previousCount += 1));
  };

  return (
    <div>
      <h1> My favourite colour has been updated {count} times.</h1>
      <h1>My favorite colour is {colour}</h1>

      <button type="button" onClick={() => changeColour("green")}>
        GREEN
      </button>
      <button type="button" onClick={() => changeColour("yellow")}>
        YELLOW
      </button>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<FavouriteColour favouriteColour={"yellow"} />);
Enter fullscreen mode Exit fullscreen mode

This is, in fact, the better way of achieving the desired behaviour in this case. The previous example only aimed to show how the useEffect works.

Conclusion

React components go through three phases in their lifecycle. These are mounting, updating and unmounting. Each of the phases is associated with certain methods which are executed in specific order and can be used to modify the state
and behaviour of the components.

These methods, however, are only for class-based components. For functional components the lifecycle methods are replaced by hooks.

Hooks have gaining popularity because they are cleaner and less verbose. Even though there are several default hooks provided by React, the most commonly used ones are useState and useEffect.

Top comments (0)