Everything you need to know about setState()
I found mutating the state can be very tricky and can possibly introduce a lot of problems. In this article, I am going to talk about the fundamental things you need to know about
setState(). This includes some properties of
setState() and detailed explanations about
setState() in different lifecycles.
Let’s begin our journey and start with something easy.
What does setState() do?
To better understand what
setState() does, let’s first take a look at what the state of a React component is.
The state allows React components to change their output over time in response to user actions, network responses, etc, without violating their rules. It can be initialized in the constructor of a React component and afterward, can be accessed via the React component’s class instance with the
I know it might sound very complex if you are a beginner to React. So here is a simple example to help better explain it.
As you can see from this example, we initialize the state in the constructor and access it by using
this.state.list in the render function.
Every time the state changes, the render method will run again and display the correct state in your browser. So in this case, when
this.state.list changes, the UI will change correspondingly because of it being re-rendered.
When changing the state we cannot directly mutate the
this.state. Instead, we should use
setState(). One reason is, for a pure component, directly mutating state won’t trigger the component re-render, which could cause some odd bugs.
setState() we can change the state without directly mutating it. This will lead to the re-rendering of the component due to the change of the state. However, we have to be extremely careful with it. Sometimes not using
setState() properly will not only fail your goals but also generate some performance issues. So why is that? What kind of problems can we have?
Why do we need to be careful?
The most severe problem I have encountered so far by using
setState() is crashing the website with an infinite loop. Since
setState() triggers re-render, it is very easy to cause an infinite loop if it happens in the wrong lifecycle. We will take a deep look into lifecycles in the next section to see how it affects the performance.
Another thing we need to keep in mind is that
setState() is asynchronous. Not being familiar with this property can bring us lots of trouble as well. It will not only introduce some errors but also cause some performance issues if we assume
setState() is synchronous and expect the result right away.
Even though we put
setState(), it might not print out the latest state since the console log will execute before the state mutation. This is due to the asynchronism of
setState() which cannot guarantee the mutation of state to act at the moment. In this case, React is doing its job and executes
console.log() first and leaving
setState() for the next event loop.
One way we can do this is to let React wait for the result of the new state and invoke the
console.log() till then.
Which lifecycle is good for setState() and what are the differences?
Now let’s look at something fancier. We are going to go through what the React lifecycle is and why it is important for
When developing in React, every component follows a cycle from when it is created and mounted on the DOM to when it is unmounted and destroyed. This can be broadly categorized into three parts: mounting, updating and un-mounting.
Here is a diagram demonstrating the main idea:
- A component mounts when it is created and first inserted into the DOM. Like when the component is rendered for the first time.
- Updating methods is when props change causing the component to be re-rendered. The updating lifecycle methods give you control over when and how this updating should take place.
- When components are removed from the DOM, the un-mounting method will help us handle the un-mounting of components.
So when and how to put
setState() at different lifecycles? Here is a brief summary I have for whether we can use
setState() for each lifecycle.
Let’s start with something easy and more commonly used; the
constructor, we should avoid using
setState() because this is the only place we directly assign the initial state to
Also, we cannot directly put it in
render() either since changing state each time triggers re-rendering which calls
setState() again. This will result in an infinite loop.
However, we can use it in render method by using the
setState() where we are assigning the props or attributes of other elements.
Below is an example that uses
setState() in the
render() method indirectly.
So what about other lifecycles? Let’s take a look at when these lifecycles will be invoked and how to use
setState() at different lifecycles.
ComponentWillMount() is invoked just before the mounting occurs. Calling
setState() usually won’t trigger an extra rendering because of the invoking order of this lifecycle, but it is usually not recommended to put
setState() here due to the reason that initializing the state in
constructor() can usually do a similar thing. However, if we want to implement functionality like subscription, it is better to use
componentDidMount() instead. Because if we need to subscribe to something, we usually need to unsubscribe from it in
componentWillUnmount() are not necessarily paired. When
componentWillUnmount() is not being called due to server rendering or async rendering, it will result in memory leaks. However,
componentDidMount() can solve it by guaranteeing
componentWillUnmount() will later be called for clean up.
In the new version of React(v16.9.0), it is removed and
componentDidmount() is recommended to use instead.
ComponentDidMount() is invoked immediately after a component is mounted. In
setState() won’t introduce any errors but it might cause performance issues because it will trigger an extra rendering and this will happen before the browser updates the screen. That is to say, the user won’t see the intermediate state but
render() will be called twice in this case.
ComponentDidUpdate() is invoked immediately after updating occurs. This is a tricky place to put
setState() because it is very easy to cause an infinite loop. It must be wrapped in a condition like below to avoid such a situation:
ComponentWillReceiveProps() is a method that is called before a component does anything with the new props. This method is called with the new props passed as an argument. To use
setState() here it is better to check if the props change, otherwise it will change the state as long as new props value is passed and it can trigger unnecessary rendering.
ComponentWillUpdate() is a method that can be used to perform preparation before re-rendering occurs. You cannot call
this.setState in this method.
Note that one side-effect when props change is
componentWillReceiveProps() might get called multiple times for a single update while
componentDidUpdate() is guaranteed to be invoked only once per update. Therefore, it is a good idea to move data update from
getDerivedStateFromProps()(in React v16 upwards).
In React v16 upwards,
componentWillReceiveProps() is removed and
getDerivedStateFromProps() is introduced. We cannot use
this.setState() here since it is a static method, instead, we directly return the updated state data.
GetDerivedStateFromProps() is called both on initial mounting and on re-rendering of the component, so you can use it instead of creating the state based on props in the constructor. If you declare both
getDerivedStateFromProps() will be called and a warning will be shown in the console.
It might sound really complex if you are a beginner to React, so it is highly recommended to check the table shown above if you are wondering if you can use
setState() at a certain place and then read the corresponding explanation.
I hope you have more fun and easier development experience after reading this article.