State Reducer Pattern in Advanced React0%

State Reducer Pattern in Advanced React

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

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 useState or 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.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext