The useState Hook: A Deep Dive into React State Management #2
In the previous article <link> you have learned how to set up your first React environment and run your app using CRA.
useState
is a hook in React that allows you to add state to functional components. It returns a pair of values: the current state and a function that can be used to update the state. Here's an example of how you might use useState
to add a piece of state called message
to a functional component:
import React, { useState } from 'react';
function MyComponent() {
const [message, setMessage] = useState('Hello World');
return (
<div>
<p>{message}</p>
<button onClick={() => setMessage('Hello from the button!')}>
Update message
</button>
</div>
);
}
In this example, message
is the current state value, and setMessage
is a function that can be used to update the value of message
. As an initial state we are using ‘Hello World’ string, although the initial state does not need to be defined, you can use type instead (string, bool, array etc.). So if we we’re going to set the type for message
it would be just useState('')
.
When the button is clicked, the setMessage
function is called with the new value for message
, which updates the state and causes the component to re-render with the new value.
It’s important to note that useState
should be called inside the body of a functional component, not in the top-level scope or in any other part of the code.
useState caveats
- when using
useState
is that the state update may not be applied immediately. This can be a problem if you're relying on the updated state value in the same function or block of code where you're calling the state update function. - For example, consider the following code:
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
console.log(count); // Logs 0, not 1
}
In this example, the increment
function increments the value of count
by 1, but the state update may not be applied immediately. This means that the console.log
statement will log the old value of count
, not the updated value.
To work around this problem, you can use the second argument to the setState
function, which is a callback that will be executed after the state update is applied. For example:
function increment() {
setCount(count + 1, () => {
console.log(count); // Logs the updated value of count
});
}
Alternatively, you can use the useEffect
hook to perform an action after the state update is applied. For example:
useEffect(() => {
console.log(count); // Logs the updated value of count
}, [count]);
- The state returned by
useState
is always a single value. If you need to store multiple values in state, you can use an object or an array to hold the values, and spread operator to update them. For example:
const [state, setState] = useState({ count: 0, message: 'Hello World' });
setState({ ...state, count: state.count + 1 })
console.log(state) // count: 1, message: 'Hello World'
If you were going only to update the count without copying the previous state, then the message state would get removed.
setState({ count: state.count + 1 })
console.log(state) // count: 1
useState
only works with simple values (numbers, strings, booleans, etc.). If you need to store more complex values in state (such as objects or arrays), you should be careful not to modify them directly, because this can cause unexpected behavior. Instead, you should create a new copy of the value and update the state with the new copy. For example:
const [items, setItems] = useState([1, 2, 3]);
const newItems = [...items, 4];
setItems(newItems)
console.log(items) // [1,2,3,4]
And without the spread operator:
const [items, setItems] = useState([1, 2, 3]);
const newItems = [4];
setItems(newItems)
console.log(items) // [4]
useState
should only be used for values that change within the component. If you need to store values that come from outside the component (such as props or context), you should use theuseEffect
hook to update the state when the values change. TheuseEffect
hook is going to be covered in the next article!
Practice task
And now it’s time to better remember the collected information, for this purpose I have created a very simple and short task that will help you consolidate knowledge in the field of useState
.
- In your repo, create functional component called
Counter
. Open thesrc/App.js
file and import newly created component and theuseState
hook from thereact
package. - In
Counter
component, call theuseState
hook you can call the valuescount
andsetCount
. The initial value of count should be 0. - In the JSX, display the current value of
count
and add anonClick
handler to the button that increments the value ofcount
using the updater function returned byuseState
. - If that was too easy, add two more handlers to your
Counter
. The first one to decrement the currentcount
value, and the second to reset the count value to the initial state.
Here’s what the Counter
component should look like (basic):
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<p>Count: {count}</p>
</div>
);
Bonus:
import React, { useState } from 'react';
const INIT_STATE = 0;
function Counter() {
const [count, setCount] = useState(INIT_STATE);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
<button onClick={() => setCount(INIT_STATE)}>
Reset
</button>
<p>Count: {count}</p>
</div>
);
I hope it was easy for you to understand how the useState
works. In the next article I will cover the useEffect
hook.