State management mistakes compound quietly. A component starts simple, then duplicated values, impossible states, and deep objects make every change risky. This guide lists the mistakes that make React apps hard to maintain and the smaller pattern that fixes each.
The fixes are mostly structural: keep one source of truth per value, update it immutably, and compute the rest.
Storing values you can derive
A derived value is one you can calculate from props or other state during render. Storing it means updating the copy every time the source changes, and the two fall out of sync the moment you forget.
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(firstName + " " + lastName);
}, [firstName, lastName]);The Effect runs after render, so the component paints once with a stale value, then again with the new one. The fix is a plain variable computed during render, with no state and no Effect. The pattern is covered in derived state in React.
Contradictory state
Two booleans that should never both be true leave room for an impossible state. The classic case is a form that tracks isSending and isSent separately.
const [isSending, setIsSending] = useState(false);
const [isSent, setIsSent] = useState(false);A missed update leaves both true at once. Replace them with one status value that can be typing, sending, or sent, and derive the booleans when you need them. Deriving the booleans from a status string also removes the class of bug where one update is forgotten.
Mirroring props into state
Initializing state from a prop only copies the first value. Later prop changes are ignored, so the two drift apart.
function Message({ messageColor }) {
const [color, setColor] = useState(messageColor);
}Use the prop directly instead. Mirror into state only when you truly want to ignore later updates, and name the prop with an initial prefix to make that intent clear. When the prop is the source of truth, the component can never disagree with its parent.
Duplicating data across state
Holding the same object in two places means every edit must update both. Store the id and find the rest during render.
const [items, setItems] = useState(initialItems);
const [selectedItem, setSelectedItem] = useState(items[0]);The fix keeps selectedId in state and finds selectedItem from items. Editing an item then updates the list and the selection together, because there is only one copy of the data.
Mutating state in place
React compares state by reference. Pushing into an existing array and calling the setter with the same reference looks like no change.
function addTodo(todo) {
todos.push(todo);
setTodos(todos);
}Return a new array with spread or concat, and replace nested objects instead of editing them. A new reference is what tells React the value changed, so every update must produce one. Immutable updates are covered in how to update objects in React state.
Syncing state with an Effect
Using an Effect to copy state into another state variable restarts the render cycle and hides the real dependency.
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
setVisibleTodos(getVisibleTodos(todos, filter));
}, [todos, filter]);Compute visibleTodos during render instead. The Effect is only for synchronizing with an external system, not for deriving one piece of state from another.
Server cache in a client store
Server data has its own lifecycle: loading, caching, refetching, and errors. A client store holds a copy that nothing keeps in sync with the server.
Keep server data in a query library such as TanStack Query, and leave client stores for values only the user changes. The boundary is separate from scope questions like how to avoid global state when local state is enough.
A checkable summary
- Store the source, derive the rest.
- One status value instead of contradictory booleans.
- Prefer ids over duplicated objects.
- Never mutate state in place.
- No Effects just to sync state.
- Server data in a query library, client data in React state or a store.
Each mistake is fixed by the same idea: keep one source of truth per value and let everything else compute from it.
Rune AI
Key Insights
- Compute derived values during render instead of storing them.
- Replace contradictory booleans with one status value.
- Never mutate arrays or objects in place.
- Keep server cache in a query library, not a client store.
Frequently Asked Questions
What is redundant state in React?
Why is mutating state a mistake?
Should server data live in a client store?
Conclusion
Most state management mistakes come from storing what can be derived, duplicating data, mutating in place, or reaching for Effects to sync state. Keep one source of truth per value and update it immutably.
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.