Published
- 5 min read
How To Use the useState Hook in React
Introduction
React is a world made up of components.
Components themselves maintain their own ‘state’.
What is ‘state’, you might ask?
The state is nothing but a plain old JavaScript object used to keep a record of the data contained in the component.
This data might remain the same or update as a consequence of some action performed by the user.
State can be maintained by both class and functional components in React starting from React 16.8.
As the React team is leaning more towards using functional components rather than class components, let’s see how the state is managed in functional components.
State in Functional Components
Suppose you want to track the number of times the user clicks a button.
You could maintain a ‘count’ variable as the component’s state variable to track the number of clicks.
You update the ‘count’ variable in the state every time the user clicks the button.
Let’s see what the syntax would look like:
import './styles.css'
import { useState } from 'react'
export default function App() {
const [count, setCount] = useState(0)
const updateCountHandler = () => {
setCount(count + 1)
}
return (
<div className='container'>
<button onClick={updateCountHandler}>Click</button>
<p>No. of clicks: {count}</p>
</div>
)
}
In the code snippet above, the component maintains its state using the statement:
const [count, setCount] = useState(0)
Let’s understand what’s happening in this code snippet from right to left:
First, we see the useState hook called with an argument of 0.
‘useState’ is a built-in hook provided by React.
The argument passed to it is the value we want our state to be initialized with during the first render. Since ‘count’ is a number, we want it to start from zero. Providing an initial value is entirely optional.
Moving on to the assignment operator’s left-hand side, we see a strange syntax with the square brackets i.e.,
const [count, setCount]
The above syntax might look confusing, so let me explain it.
When we call the useState hook with an initial value, it returns an array.
That array contains our state variable, ‘count’, and a function to update the count variable ‘setCount’.
The below code snippet is equivalent to the square bracket code snippet.
const countState = useState(0)
const count = countState[0] // count state variable
const setCount = countState[1] // count state updater function
If you have multiple states to maintain in a single component this syntax soon becomes confusing and difficult to keep track of.
It then becomes convenient to use the square bracket syntax, i.e. array destructuring, to avoid writing boilerplate code.
Also, you could name ‘count’ and ‘setCount’ anything as you please. It’s just good practice to prefix your state updater function with ‘set’ followed by the name of the state it’s updating to make sense of the state that is being acted upon and the function responsible for it.
Moving on to the updateCountHandler.
You can see that it’s updating the count by calling the ‘setCount’ function provided by useState and incrementing the value by one.
The updateCountHandler is passed to the onClick event attached to the button.
Each time on button’s click, the updateCountHandler will call ‘setCount’ to update the count by one.
The ‘count’ state variable provided by ‘useState’ will reflect the updated value.
If you want to display the ‘count’ value, you could use ‘count’ provided by the useState function in your JSX.
React state updates happen in batches.
That means you might not see the updated state value as soon as expected.
Let’s understand it with one more example.
Suppose our button now performs some asynchronous operation on click and we need to calculate the number of clicks for the same button.
To simulate the async nature we can add a timeout of 2 seconds.
Based on our understanding of useState hook till now we might come up with a solution that looks like this:
import './styles.css'
import { useState } from 'react'
export default function App() {
const [count, setCount] = useState(0)
const updateCountHandler = () => {
setTimeout(() => {
setCount(count + 1)
}, 2000)
}
return (
<div className='container'>
<button onClick={updateCountHandler}>Click</button>
<p>No. of clicks: {count}</p>
</div>
)
}
Going by the above code snippet you might expect the value of the ‘count’ to be in sync with the number of clicks.
But that does not happen.
It doesn’t work as expected because React’s state updates are not instantaneous and are in batches. It takes time to reflect the current state value in ‘count’.
As ‘count’ does not have the latest state value we increment the stale value of ‘count’ by 1.
React supplies us with one more variant of the useState hook to avoid this.
Let’s see it in action below.
import './styles.css'
import { useState } from 'react'
export default function App() {
const [count, setCount] = useState(0)
const updateCountHandler = () => {
setTimeout(() => {
setCount((prevCount) => prevCount + 1)
}, 2000)
}
return (
<div className='container'>
<button onClick={updateCountHandler}>Click</button>
<p>No. of clicks: {count}</p>
</div>
)
}
Here’s the code snippet which makes all the difference:
setCount((prevCount) => prevCount + 1)
We pass an arrow function to this variant of the ‘setCount’ updater function. React plugs in the previous state value here irrespective of if it’s being reflected in the ‘count’ state variable.
‘prevCount’ could be named anything. The only thing to remember is that the function’s first argument passed to the state updater function will always be the previous state value.
Takeaway
useState is one of the most frequently used hooks in React, hence an important one.
I hope this article on useState has given you a good basic understanding of the hook works in React.
If you want to explore more about it, I recommend checking out the references below.
Till then, Happy Coding!