Concept · Building apps
Derived state vs effects
If you can compute it during render, don’t store it and don’t sync it with an effect.
Derived state is any value you can calculate from props or state you already have: a filtered list, a total, a full name, whether a form is valid. The rule: compute it during render; don't store it in state, and don't keep it in sync with an effect.
// ✗ Duplicated state, synced by an effect
const [visible, setVisible] = useState([])
useEffect(() => {
setVisible(todos.filter((t) => !t.done || showDone))
}, [todos, showDone])
// ✓ Derived during render
const visible = todos.filter((t) => !t.done || showDone)The effect version renders twice for every change: once with the stale list and once with the right one. It needs a correct dependency array, and it can drift out of sync. The derived version is always correct. If the calculation is genuinely expensive (measure first), wrap it in useMemo.
What effects are for
useEffect is for synchronizing with something outside React: a subscription, a WebSocket, a third-party widget, document.title, or a timer. A useful test is whether the code would need to run even if nothing re-rendered. If it's just turning some data into other data, it's not an effect.
| You want to… | Do this instead of an effect |
|---|---|
| Compute a value from props/state | Compute it in render (useMemo if expensive) |
| Respond to a click or submit | Do it in the event handler |
| Reset state when a prop changes | Give the component a key |
| Fetch data | Use a data library or framework loader (server state) |
| Notify a parent of a change | Call the parent's callback in the same event handler |
Choosing where state lives
- Keep a single source of truth. Store the minimal state: the selected ID, not a copy of the selected object that goes stale when the list updates.
- Lift state up to the nearest common parent when two components need it, and move it down when only one does.
- Don't copy props into state (
useState(props.value)). The copy ignores later prop changes. Either read the prop directly or make the component fully controlled.
Fewer effects mean fewer extra renders and fewer race conditions. See rendering and reconciliation for what each render costs.