Lesson 5 / 25
createSlice, Actions and Immer
Reducers and action creators in one place.
Write mutating code, get immutable updates
createSlice takes a name, an initialState and a reducers object. For each reducer it generates an action creator and an action type such as cart/itemAdded. Reducers run inside Immer, so you may write state.items.push(item): Immer records the changes on a draft and produces a new immutable state. Either mutate the draft or return a new value, not both. extraReducers (a builder callback in Redux Toolkit 2) lets a slice respond to actions defined elsewhere, such as thunks.
A cart slice
cartSlice.ts.
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
type CartItem = { id: string; qty: number };
type CartState = { items: CartItem[] };
const initialState: CartState = { items: [] };
const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
itemAdded(state, action: PayloadAction<CartItem>) {
const existing = state.items.find((i) => i.id === action.payload.id);
if (existing) existing.qty += action.payload.qty; // Immer makes this safe
else state.items.push(action.payload);
},
itemRemoved(state, action: PayloadAction<string>) {
state.items = state.items.filter((i) => i.id !== action.payload);
},
cleared() {
return initialState; // returning a new value is also allowed
},
},
});
export const { itemAdded, itemRemoved, cleared } = cartSlice.actions;
export default cartSlice.reducer;Editing a photocopy
Immer hands you a photocopy (the draft) to scribble on. When you finish, it produces a clean new original containing your edits, while the old original stays untouched.
Quick check: Why can createSlice reducers write state.items.push(x)?
- Redux allows mutation since version 2
- They run inside Immer, which turns draft mutations into an immutable update
- push is overridden by Redux
- The state is deep-cloned before every action
Answer
They run inside Immer, which turns draft mutations into an immutable update — Immer tracks changes to a draft.