Lifecycle in React Component
Yohanes Setiawan
Posted on April 14, 2022
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)
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);
}
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>
);
}
}
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>
);
}
}
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>
}
}
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>]
}
}
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>
}
}
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>
);
}
}
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>
);
}
}
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'))
}
}
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>
);
}
}
When you click Detonate Building 💣
button, 'The building is destroyed 💥'
text will be logged into our console.
Posted on April 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024