The useReducer
hook is a React hook that provides an alternative to useState
for managing state in a React component. It is typically used when state logic is complex, or when the state depends on the previous state. It is commonly used for managing state in larger applications or in situations where multiple state transitions need to be handled in a predictable and structured way, such as in forms, complex stateful logic, or managing global state in a component.
1. What is the useReducer
Hook?
The useReducer
hook is very similar to useState
, but it provides more control over state transitions by using a reducer function. It follows the same pattern as Redux, where actions are dispatched to update the state. Instead of directly mutating the state, useReducer
uses a reducer function that returns a new state based on the action dispatched.
Basic Syntax of useReducer
:
const [state, dispatch] = useReducer(reducer, initialState);
state
: The current state of the component.dispatch
: A function that sends actions to the reducer.reducer
: A function that takes the current state and an action, and returns the next state.initialState
: The initial state value.
2. Using useReducer
Example: Basic Counter with useReducer
import React, { useReducer } from 'react';
// Reducer function
const counterReducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
};
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
export default Counter;
In this example:
counterReducer
is a function that receives the current state and an action, and returns a new state.- The
dispatch
function sends an action to the reducer to update the state.
3. Understanding the Reducer Function
A reducer function is a pure function that receives two arguments:
- State: The current state.
- Action: An object representing the action to be performed, which typically has a
type
property and can include additional data (payload).
The reducer function returns the updated state based on the action’s type.
Example: Reducer with Payload
const reducer = (state, action) => {
switch (action.type) {
case 'setName':
return { ...state, name: action.payload };
default:
return state;
}
};
function App() {
const [state, dispatch] = useReducer(reducer, { name: '' });
return (
<div>
<p>Name: {state.name}</p>
<button onClick={() => dispatch({ type: 'setName', payload: 'John' })}>
Set Name
</button>
</div>
);
}
Here:
- The action
type: 'setName'
updates thename
property in the state. - The
payload
carries the new name.
4. When to Use useReducer
vs useState
useState
: Great for simpler state logic where the state change is relatively straightforward, and it doesn’t involve complex transitions.useReducer
: Better when:- The state logic is more complex (e.g., dependent on previous state).
- You need to manage multiple state variables that are related.
- You want to centralize your state updates in a single function (the reducer).
- You have complex state updates involving different actions and payloads.
5. Using useReducer
with Multiple Actions
In real applications, you might need to handle different kinds of actions. useReducer
can be extended to handle multiple action types, making it more scalable for complex state management.
Example: Handling Multiple Actions
import React, { useReducer } from 'react';
const initialState = { count: 0, text: '' };
// Reducer function to handle multiple actions
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'setText':
return { ...state, text: action.payload };
default:
return state;
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
<p>Text: {state.text}</p>
<input
type="text"
value={state.text}
onChange={(e) => dispatch({ type: 'setText', payload: e.target.value })}
/>
</div>
);
}
export default App;
In this example:
- The state is updated based on multiple action types:
increment
,decrement
, andsetText
. - Each action type triggers a different update to the state.
6. useReducer
with useContext
for Global State
useReducer
can also be used in combination with useContext
to manage global state across your React application.
Example: Using useReducer
with useContext
import React, { useReducer, useContext } from 'react';
// Create context
const GlobalStateContext = React.createContext();
// Reducer function
const globalReducer = (state, action) => {
switch (action.type) {
case 'toggleTheme':
return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
default:
return state;
}
};
function App() {
const [state, dispatch] = useReducer(globalReducer, { theme: 'light' });
return (
<GlobalStateContext.Provider value={{ state, dispatch }}>
<ThemeSwitcher />
</GlobalStateContext.Provider>
);
}
function ThemeSwitcher() {
const { state, dispatch } = useContext(GlobalStateContext);
return (
<div>
<p>Current Theme: {state.theme}</p>
<button onClick={() => dispatch({ type: 'toggleTheme' })}>Toggle Theme</button>
</div>
);
}
export default App;
In this example:
useReducer
is used to manage the theme state.useContext
is used to provide and consume the state globally across components.