JAVASCRIPT
Manage Complex State with `useReducer` Hook
Efficiently manage intricate state logic in React components using the `useReducer` hook. This snippet demonstrates its power for handling multiple state transitions cleanly, providing a predictable state container pattern similar to Redux.
import React, { useReducer } from 'react';
// 1. Define initial state
const initialState = {
count: 0,
text: 'Hello Reducer',
items: [],
};
// 2. Define a reducer function
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'reset':
return { ...state, count: initialState.count };
case 'setText':
return { ...state, text: action.payload };
case 'addItem':
return { ...state, items: [...state.items, action.payload] };
case 'removeItem':
return { ...state, items: state.items.filter(item => item !== action.payload) };
default:
throw new Error('Unknown action type');
}
};
// 3. Create a component that uses useReducer
function CounterWithReducer() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '5px' }}>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: 'increment' })} style={{ marginRight: '10px' }}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })} style={{ marginRight: '10px' }}>Decrement</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset Count</button>
<h2 style={{ marginTop: '20px' }}>Text: {state.text}</h2>
<input
type="text"
value={state.text}
onChange={(e) => dispatch({ type: 'setText', payload: e.target.value })}
style={{ padding: '8px', width: '300px' }}
/>
<h2 style={{ marginTop: '20px' }}>Items</h2>
<button onClick={() => dispatch({ type: 'addItem', payload: `Item ${state.items.length + 1}` })}>Add Item</button>
<ul style={{ listStyleType: 'disc', paddingLeft: '20px' }}>
{state.items.map((item, index) => (
<li key={index} style={{ margin: '5px 0' }}>
{item}
<button onClick={() => dispatch({ type: 'removeItem', payload: item })} style={{ marginLeft: '10px', background: '#dc3545', color: 'white', border: 'none', borderRadius: '3px', padding: '5px 10px', cursor: 'pointer' }}>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
export default CounterWithReducer;
How it works: The `useReducer` hook is an alternative to `useState` for managing more complex state logic that involves multiple sub-values or when the next state depends on the previous one. It takes a reducer function and an initial state, returning the current state and a `dispatch` function. The `dispatch` function is used to send 'actions' to the reducer, which then computes the new state based on the action type and payload. This pattern promotes predictable state changes and is particularly useful for state transitions that are coupled or require specific sequences, making your state management more organized and testable than deeply nested `useState` calls.