How to Update Redux Reducers in React
This article provides a clear, step-by-step guide on how to update Redux reducers in a React application. You will learn the core rule of state immutability, how to update state using traditional Redux switch-case statements, and how to simplify the process using modern Redux Toolkit.
Understanding the Core Rule: Immutability
In Redux, reducers must be pure functions. This means they take the current state and an action as arguments, and return a brand-new state object. You must never mutate the existing state directly.
Instead of doing this:
// WRONG: Direct mutation
state.value = action.payload;
return state;You must do this:
// CORRECT: Returning a new object
return {
...state,
value: action.payload
};Method 1: Traditional Redux (Switch-Case)
In traditional Redux, you use the JavaScript spread operator
(...) to copy existing state properties and override the
specific values you want to update.
Here is an example of a counter reducer updating state using a switch-case statement:
const initialState = {
count: 0,
user: "Guest"
};
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return {
...state, // Copy existing state (keeps user as "Guest")
count: state.count + 1 // Update only the count
};
case 'DECREMENT':
return {
...state,
count: state.count - 1
};
default:
return state;
}
}Method 2: Modern Redux (Redux Toolkit)
The official and recommended way to write Redux today is with Redux Toolkit (RTK). Redux Toolkit uses a library called Immer under the hood.
Immer allows you to write “mutative” code (like
state.value = 123) inside your reducers, but it
automatically translates it into a safe, immutable update. This
eliminates the need to use the spread operator for complex, nested state
objects.
Here is how you write and update a reducer using Redux Toolkit’s
createSlice:
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: {
count: 0,
user: "Guest"
},
reducers: {
increment: (state) => {
// Immer allows you to "mutate" the state directly safely
state.count += 1;
},
decrement: (state) => {
state.count -= 1;
},
updateUser: (state, action) => {
state.user = action.payload;
}
}
});
// Export actions to be dispatched in React components
export const { increment, decrement, updateUser } = counterSlice.actions;
// Export the reducer to be added to the Redux store
export default counterSlice.reducer;How to Dispatch the Updates in React
To trigger these reducer updates from your React components, use the
useDispatch hook from the react-redux
library.
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, updateUser } from './counterSlice';
function CounterComponent() {
const count = useSelector((state) => state.counter.count);
const user = useSelector((state) => state.counter.user);
const dispatch = useDispatch();
return (
<div>
<p>User: {user}</p>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(updateUser('John Doe'))}>Change User</button>
</div>
);
}