React hooks allow us to create much cleaner code than when using class components (up to 90% cleaner).
We’ll explore the
useState hook today by comparing two versions of the same counter component. One of them is written using the class syntax and the other using hooks.
Counter (Class): https://codepen.io/alveem/pen/VwayxYx
Counter (Hooks): https://codepen.io/alveem/pen/OJNzvqa
Here’s what the class based counter’s code looks like:
And here’s what the
useState version looks like:
useState method takes in an initial state value and returns an array where the first element is a reference to the state and the second element is a function to change the state.
Note that we’re destructuring the array to make the code cleaner. It’s the same as the following:
Let’s see whether the asynchronous behavior of state change is affected by hooks.
We want to increase our counter by
2 instead of
1 now. Let’s add another
this.setState statement to our
But our counter still only increases by
setState is asynchronous. Since
Object.assign to assign the state updates it ends up doing this:
The last object with a matching key passed to
Object.assign is used to update the original object’s value.
Let’s try passing a function to
And now our counter increases by
Now we want to do the same using
useState. We’ll call
setCount a second time inside our
And the counter only increases by
1. So this works the same way as when we passed an object to
setState in a class component.
setCount function can also has a different form.
This works now and our counter increases by
There are some differences in the behavior of
setState and the setter function returned by
setCount function in our example).
setState method passes in the props as the second argument to the callback function passed to it.
this.setState((state, props) => console.log(props))
State Update Behavior
As mentioned above,
setState merges objects to update state. Hooks replace the entire state.
Let’s try to limit our counter’s
decrease method so it doesn’t go below
Class Component (setState)
Here’s what the method would look in the class component.
Notice that on line 3 we’re not returning anything. The state still updates since React merges the old state with whatever is returned.
Functional Component (useState)
What happens when we do something similar in our functional component?
When the counter is at
0 and we try to decrease it, the number disappears.
The new state is set to
undefined since whatever we return in callback function passed to
setCount is set as the new state. And if we try to increase the counter we’ll get a
Instead, you need to return the entire new state because new values aren’t automatically merged as in
And now it works as expected.
If you use an object with multiple values in
useState, a new object needs to be passed in that contains the old key-value pairs along with the new key-value pair(s).
For managing complex state, the Hooks docs recommend using
- How Are Function Components Different From Classes?
- Should I Use Multiple State Variables in
- React Hooks Docs
- Rules of Hooks (from the docs)
- Don’t call Hooks inside loops, conditions, or nested functions.