The Lifecycle of React Components
Tags: react, lifecycle, hooks, webdev
Published: 2023-03-03 12:48:47


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"

```js
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 />);
```

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".

```js
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"} />);
```

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.

```js
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"} />);
```

### 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".

```js
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"} />);
```

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.

```js
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 />);
```

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.

```js
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"} />);
```

### 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`:

```js
const [value, setValue] = useState(initialState);
```

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

```js
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"} />);
```

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.

```js
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"} />);
```

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`.

```js
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"} />);
```

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`.