Runnable lesson · press Try it on the examples to edit and run them here.
A component often has several pieces of state. Do you create one useState for each, or a single object? Both work. This lesson gives you a way to decide.
Several separate states
You can call useState as many times as you need:
function App() {
const [name, setName] = React.useState("Ada");
const [age, setAge] = React.useState(36);
const [city, setCity] = React.useState("London");
return (
<div>
<p>{name}, {age}, {city}</p>
<button onClick={() => setAge(age + 1)}>Birthday</button>
</div>
);
}
Each useState is independent. Changing age does not touch name. This style is simple, and each piece is easy to update.
One object for related values
When values change together or belong together (a form with many fields, coordinates, a payload), one object is neater:
function App() {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
return (
<div>
<p>x: {position.x}, y: {position.y}</p>
<button onClick={() => setPosition({ ...position, x: position.x + 10 })}>Move right</button>
</div>
);
}
Because the setter replaces the value, you must copy the other fields with the spread operator. Forgetting that is the classic bug; see Updating Objects in State.
How to choose
| Question | Answer suggests |
|---|---|
| Do the values always change together? | One object |
| Are they unrelated (a name and a dark-mode switch)? | Separate states |
| Do you update one at a time, often? | Separate states are simpler |
| Does one field’s change depend on another? | An object or useReducer |
| Will you send them together to a server? | An object is convenient |
Do not duplicate information
Keep state minimal. These three would be a mistake:
const [items, setItems] = useState([]);
const [itemCount, setItemCount] = useState(0); // duplicate! use items.length
const [hasItems, setHasItems] = useState(false); // duplicate! use items.length > 0
Update one and forget the others, and your screen lies. Calculate items.length instead. See Derived State.
Related states and impossible combinations
Beware states that can contradict each other:
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
// Possible but invalid: isLoading and isError both true
One state with a named value prevents impossible combinations:
function App() {
const [status, setStatus] = React.useState("idle"); // "idle" | "loading" | "success" | "error"
function start() {
setStatus("loading");
setTimeout(() => setStatus("success"), 1000);
}
return (
<div>
<p>Status: {status}</p>
<button onClick={start} disabled={status === "loading"}>Start</button>
</div>
);
}
A single status string can only be one thing at a time. This is used constantly with data fetching; see Loading States.
When it gets complicated: useReducer
If you have many related states and many ways to change them, move the logic into a reducer with useReducer, which keeps all the update rules in one function: Reducers and useReducer.
Key points
- Use separate
useStatecalls for unrelated values; use an object for values that belong and change together. - Never store values you can compute from other state.
- Prefer one
statusvalue over several booleans that could contradict each other.