Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
A reducer is a pure function that takes the current state and an action, then returns the next state. Think of it as a state update machine that follows strict, predictable rules.
The existing state before any changes
An object describing what happened
The new state after applying the action
Not all state needs a reducer! Use them when your state logic becomes complex and hard to manage with multiple useState calls.
The useReducer hook is React's built-in way to use reducers in your components. It returns the current state and a dispatch function.
The current state value (like useState)
Function to send actions to the reducer
A simple counter showing basic reducer concepts
function Counter() {
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: (state?.count || 0) + 1 };
case 'DECREMENT':
return { count: (state?.count || 0) - 1 };
case 'RESET':
return { count: 0 };
case 'SET_VALUE':
return { count: Math.max(0, action.payload) };
default:
return state || initialState;
}
}
const [state, dispatch] = React.useReducer(counterReducer, initialState);
return (
<div className="container">
<h1>๐ข Counter with Reducer</h1>
<div className="counter-display">
<div className="count-value">{state?.count || 0}</div>
<div className="count-label">Current Count</div>
</div>
<div className="button-grid">
<button
onClick={() => dispatch({ type: 'INCREMENT' })}
className="btn btn-primary"
>
โ Increment
</button>
<button
onClick={() => dispatch({ type: 'DECREMENT' })}
className="btn btn-secondary"
>
โ Decrement
</button>
<button
onClick={() => dispatch({ type: 'RESET' })}
className="btn btn-danger"
>
๐ Reset
</button>
<button
onClick={() => dispatch({ type: 'SET_VALUE', payload: 10 })}
className="btn btn-info"
>
โก Set to 10
</button>
</div>
<div className="action-log">
<h3>๐ Action Log</h3>
<div className="log-content">
<div className="log-entry">
<strong>Current State:</strong> {JSON.stringify(state)}
</div>
<div className="log-entry">
<strong>Try this:</strong> Click buttons to see how actions update state
</div>
</div>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<Counter />);Loading preview...
Manage todos with add, toggle, and delete actions
function TodoList() {
const [state, dispatch] = React.useReducer(todoReducer, initialState);
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
todos: [...state.todos, {
id: Date.now(),
text: action.payload,
completed: false
}]
};
case 'TOGGLE_TODO':
return {
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case 'DELETE_TODO':
return {
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case 'CLEAR_COMPLETED':
return {
todos: state.todos.filter(todo => !todo.completed)
};
default:
return state;
}
}
const initialState = {
todos: [
{ id: 1, text: 'Learn reducers', completed: true },
{ id: 2, text: 'Build todo app', completed: false }
]
};
const [inputValue, setInputValue] = React.useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (inputValue.trim()) {
dispatch({ type: 'ADD_TODO', payload: inputValue.trim() });
setInputValue('');
}
};
const completedCount = state.todos.filter(todo => todo.completed).length;
const totalCount = state.todos.length;
return (
<div className="container">
<h1>๐ Todo List with Reducer</h1>
<div className="stats">
<div className="stat">
<span className="stat-number">{totalCount}</span>
<span className="stat-label">Total</span>
</div>
<div className="stat">
<span className="stat-number">{completedCount}</span>
<span className="stat-label">Done</span>
</div>
</div>
<form onSubmit={handleSubmit} className="add-todo">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="What needs to be done?"
className="todo-input"
/>
<button type="submit" className="add-btn">
โ Add Todo
</button>
</form>
<div className="todo-list">
{state.todos.length === 0 ? (
<div className="empty-state">
๐ No todos! Add one above.
</div>
) : (
state.todos.map(todo => (
<div key={todo.id} className={'todo-item ' + (todo.completed ? 'completed' : '')}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
className="todo-checkbox"
/>
<span className="todo-text">{todo.text}</span>
<button
onClick={() => dispatch({ type: 'DELETE_TODO', payload: todo.id })}
className="delete-btn"
>
๐๏ธ
</button>
</div>
))
)}
</div>
{completedCount > 0 && (
<button
onClick={() => dispatch({ type: 'CLEAR_COMPLETED' })}
className="clear-btn"
>
๐งน Clear Completed
</button>
)}
<div className="state-display">
<h3>๐ Current State</h3>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<TodoList />);Loading preview...
Reducers are pure functions that always return the same output for the same input.
Always return new state objects instead of modifying the existing state.
State changes happen through dispatching actions with clear types and payloads.
Reducers make state changes predictable and easier to debug in complex components.