State Reducer Pattern in Advanced React0%
State Reducer Pattern in Advanced React
Beginner12 min read•Updated: Jul 11, 2026
State Reducer Pattern in Advanced React
1. The Limits of Control Props
While Control Props give the consumer ultimate control over state, it is an "all-or-nothing" proposition:
- In uncontrolled mode, the component does everything.
- In controlled mode, the consumer must manage every single state transition manually.
What if a consumer likes 95% of a component's internal logic, but wants to intercept, modify, or block a specific state change?
For example:
- A dropdown menu should close on select, except when holding the Shift key (multi-select).
- A counter should increment freely, except it must never exceed 4 clicks without confirming a modal.
The State Reducer Pattern solves this problem by allowing the consumer to pass a custom reducer function into the component to intercept internal state transitions!
Output
Normal Transition:
[Action Dispatched] ──► [Internal Reducer] ──► [New State]
State Reducer Pattern:
[Action Dispatched] ──► [Internal Reducer calculates nextState]
│
▼
[Consumer's custom stateReducer]
(Can modify, override, or block the update!)
│
▼
[Final State Applied]
2. Implementing the State Reducer Pattern
Let's build an advanced Counter component that accepts a stateReducer:
JSX
import React, { useReducer } from 'react';
// 1. Export Action Types so consumers can reference them
export const actionTypes = {
increment: 'INCREMENT',
decrement: 'DECREMENT',
reset: 'RESET'
};
// 2. Default internal reducer
function defaultReducer(state, action) {
switch (action.type) {
case actionTypes.increment:
return { count: state.count + 1 };
case actionTypes.decrement:
return { count: state.count - 1 };
case actionTypes.reset:
return { count: 0 };
default:
throw new Error(`Unsupported action type: ${action.type}`);
}
}
// 3. Component accepting a stateReducer prop
export default function AdvancedCounter({
initialCount = 0,
stateReducer = (state, action) => action.changes // Default: accept all changes
}) {
// Enhanced reducer passes proposed changes to consumer's stateReducer!
const enhancedReducer = (state, action) => {
// A. Compute what the internal component wants to do
const changes = defaultReducer(state, action);
// B. Let the consumer intercept, inspect, and modify those changes!
return stateReducer(state, { ...action, changes });
};
const [state, dispatch] = useReducer(enhancedReducer, { count: initialCount });
return (
<div className="counter-box">
<h3>Count: {state.count}</h3>
<button onClick={() => dispatch({ type: actionTypes.decrement })}>-</button>
<button onClick={() => dispatch({ type: actionTypes.increment })}>+</button>
<button onClick={() => dispatch({ type: actionTypes.reset })}>Reset</button>
</div>
);
}
3. Consumer Example: Enforcing Business Rules
Now, a consumer can intercept transitions cleanly without taking over the entire state:
JSX
export default function RestrictedApp() {
// Custom reducer intercepts state transitions
const customReducer = (state, action) => {
// Rule: Prevent incrementing beyond 5!
if (action.type === actionTypes.increment && state.count >= 5) {
alert('Maximum threshold reached!');
return state; // Return existing state = BLOCK THE UPDATE!
}
// Otherwise, accept the component's calculated changes
return action.changes;
};
return <AdvancedCounter initialCount={0} stateReducer={customReducer} />;
}
4. Architectural Superpowers of State Reducers
- Inversion of Control: The consumer injects decision-making logic directly into the heart of the component's state machine.
- Zero Duplicate State: The consumer doesn't need to maintain their own
useStateor copy state out of the component. - Composable Customizations: Different consumers can apply totally different business policies to the exact same UI component.
Interactive Quiz
Practice Quiz
Question 1
What problem does the State Reducer Pattern solve in advanced React architecture?
Question 2
What is passed to the consumer's
stateReducer(state, action) function?Question 3
How can a consumer veto or cancel a state transition inside their
stateReducer?Question 4
Why are
actionTypes exported as a constant object in components implementing state reducers?Question 5
How does the State Reducer Pattern compare to the Control Props pattern?
Next Lesson
Prop Getters and Flexible Design Systems
Continue learning with hands-on practice, examples, and exercises in the upcoming topic.
Related Lessons
| Previous Lesson | Next Lesson |
|---|---|
| Control Props and Inversion of Control Pattern | Prop Getters and Flexible Design Systems |
Practice Quiz
Test your understanding of this lesson with 5 questions. Each question has one correct answer.