Redux & Redux Toolkit — App-Wide State ManagementEntry 6 of 11
Redux Toolkit: createSlice
createSlice defines one piece of state (a slice) with its initial state and reducers, and auto-generates the matching action creators.
Notes
- A slice groups related state and logic:
counter,auth,cart. - Each key in
reducersbecomes both a reducer case and an action creator. - RTK uses Immer, so you can write "mutating" code (
state.counter++). Immer turns it into an immutable update. - Action types are generated as
sliceName/reducerName(e.g.counter/increment), so no hand-written type strings. - Calling an action creator with an argument puts it in
action.payload.
What / Why / When
- What
Reducer + actions in one declaration.
- Why
Removes the
switch, the type strings, and manual spreading.- When
Every new Redux project. RTK is the officially recommended way to write Redux.
Code compare
// store/counter-slice.tsimport { createSlice, type PayloadAction } from "@reduxjs/toolkit";type CounterState = { counter: number; showCounter: boolean };const initialState: CounterState = { counter: 0, showCounter: true };const counterSlice = createSlice({ name: "counter", initialState, reducers: { increment(state) { state.counter++; // ✅ safe: Immer handles immutability }, decrement(state) { state.counter--; }, increase(state, action: PayloadAction<number>) { state.counter += action.payload; }, toggleCounter(state) { state.showCounter = !state.showCounter; }, },});export const counterActions = counterSlice.actions;export default counterSlice.reducer;// store/counter.actions.tsimport { createActionGroup, emptyProps, props } from "@ngrx/store";export const CounterActions = createActionGroup({ source: "Counter", events: { Increment: emptyProps(), Decrement: emptyProps(), Increase: props<{ amount: number }>(), "Toggle Counter": emptyProps(), },});Differences and gotchas
- Mutation: RTK allows
state.counter++(Immer). NgRx has no Immer; reducers must return new objects with spread. - Actions: RTK generates actions from reducers. NgRx declares actions separately with
createActionGroup, so one action can be handled by several reducers and effects. - Selectors:
createFeaturegenerates a selector per state property. RTK has no equivalent; you writestate => state.counter.counter. - ❌ In RTK, don't mutate and return a new value in the same reducer. Do one or the other.
Quick reference
| Task | Redux Toolkit | NgRx |
|---|---|---|
| Define state + reducers | createSlice({ name, initialState, reducers }) | createFeature({ name, reducer: createReducer(...) }) |
| Actions | slice.actions (auto) | createActionGroup() |
| Payload | action.payload | Named props: { amount } |
| Immutability | Immer (mutate freely) | Manual spread |
| Action type format | counter/increment | [Counter] Increment |