The choice between one state object vs multiple useState hooks depends on how often the values change together. Group values that move as a unit, and keep independent values in separate hooks. The goal is to make each update easy to get right.
The decision is rarely about saving lines. It is about how often two values must move together, because that determines how much each update has to remember.
The deciding question
Ask whether the values always change at the same time. The answer picks the structure for you.
| Situation | Prefer |
|---|---|
| Values always change together | One object |
| Values change independently | Separate hooks |
| A value is derived from others | No state, compute it |
This single question catches most mistakes. When two values must stay in sync, splitting them means every update has to remember both. Grouping them makes one update do both.
The exception is a form with an unknown number of fields, where one object or array scales more naturally than a fixed set of hooks.
Group values that change together
A cursor position has an x and a y that move as one. A single object keeps them from drifting apart.
const [position, setPosition] = useState({ x: 0, y: 0 });
function handleMove(x, y) {
setPosition({ x, y });
}Moving the pointer sets both coordinates in one call. There is no way to update x and forget y, because they live in the same object and are replaced together. Form fields for one address group the same way, because a full address usually changes as a unit.
Grouping into an object means you must copy the fields you are not changing. Updating objects in state shows the spread pattern for that.
Split values that change independently
A first name and a last name are edited by different inputs at different times. Two hooks keep them simple.
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");Each input updates only its own value, and neither update has to copy the other field. If these were one object, every keystroke would need the spread just to keep the other name intact. Separate hooks also make each handler trivial, since there is nothing to copy.
The same rule applies to a form with many fields. Fields that a user fills in separately are usually independent, so separate hooks, or a dedicated form library, read more clearly than one giant object. The moment two fields must move together, that pair belongs in one object.
Avoid impossible and redundant state
Structure also prevents contradictory values. Two booleans like isSending and isSent can never both be true, so a single status value is safer. Two booleans allow the impossible state where both are true at once, and every update must remember to clear the other.
- Values that can contradict: combine them into one.
- Values computed from others: do not store them.
- Duplicated data: keep one copy and derive the rest.
A full name is computed from first and last during render, so it should not be its own state variable. The derived state guide covers that in detail.
Avoid deeply nested state
Deeply hierarchical state is painful to update, because changing a leaf means copying every object above it. Prefer a flatter shape.
A list of places nested inside planets inside a root forces you to spread several levels for one edit. Storing each place by id in a flat map turns the same edit into a single-level copy, which is far easier to write.
What to learn next
Once the structure is right, updates become small and predictable. See useState in action for the fundamentals, then resetting state when you need to start over.
Rune AI
Key Insights
- Group values that always change together.
- Split values that change independently.
- Update an object field with the spread syntax.
- Do not store values you can derive during render.
- Avoid state that can become contradictory.
Frequently Asked Questions
Should I always group state into one object?
What happens if I forget to copy a field when updating an object?
Is a derived value state?
Conclusion
Group values that always change together into one object, and keep independent values in separate hooks. Compute derived values during render instead of storing them.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.