React reducers must be pure because React may run them more than once and may replay them in any order. A pure reducer returns the same state for the same inputs and never touches anything outside itself.
That guarantee is what lets React call the reducer again without changing the result. Any side effect in the reducer turns that safety into a bug you may only see in development.
What pure means for a reducer
A pure function has two rules: the same arguments always produce the same return value, and it does not change anything outside itself. For a reducer, that means no API calls, no timers, no random values, and no mutating the state object it receives. Reading a ref, writing a log, or generating an ID are also side effects, even when they feel harmless.
function reducer(state, action) {
switch (action.type) {
case "added":
state.items.push(action.item);
return state;
default:
return state;
}
}This reducer mutates state by pushing into its items array. The next state points at the same, now modified, object, so React sees the old reference and skips the update. In Strict Mode the mutation runs twice, so the item gets pushed twice and the bug becomes obvious.
React may call reducers twice
In Strict Mode, React calls reducers and initializers twice during development. It keeps one result and ignores the other, which is harmless for a pure function but exposes impurity immediately.
function reducer(state, action) {
switch (action.type) {
case "added":
return { ...state, items: [...state.items, action.item] };
default:
return state;
}
}This version returns a new object with a new items array. Calling it twice produces the same result both times, so the extra call changes nothing on screen. The double call happens only in development, and production calls the reducer once.
Purity is what makes reducers testable
A pure reducer is a closed box: the same state and action always produce the same result. That determinism lets you call the reducer in a test without rendering a component or mocking the browser.
Side effects belong in handlers
A reducer should only decide the next state. Showing an alert, writing to storage, or starting a request belongs in the event handler that dispatches the action.
function handleAdd(item) {
dispatch({ type: "added", item });
saveToStorage(item);
}The handler dispatches a pure action and then performs the side effect. Handlers may be impure and may run once, which is exactly why the impure work belongs there instead of in the reducer. The reducer stays deterministic, which makes it safe to test.
Read how to write actions and reducers for the authoring rules, or testing reducers without rendering components to turn that purity into fast tests.
Rune AI
Key Insights
- A pure reducer always returns the same output for the same inputs.
- React may call reducers twice in development.
- Mutating state in a reducer produces visible bugs.
- Side effects belong in event handlers.
- Purity is what makes reducers testable.
Frequently Asked Questions
What does pure mean for a reducer?
Why does React call reducers twice?
Can a reducer read the current time or generate random values?
Conclusion
Reducers run during rendering, so they must be pure. React relies on that purity to call them multiple times safely, and any mutation or side effect turns an invisible problem into a visible bug.
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.