Redux Toolkit
Redux state with simplified API. createSlice, configureStore, RTK Query for predictable state and async logic.
When to Use
- Global app state in React (slices, store, middleware)
- Async logic (RTK Query, createAsyncThunk)
- Normalizing entities with EntityAdapter
- Replacing legacy Redux
Don’t use for:
- Local state (use useState/useReducer)
- Server state caching (use React Query/SWR)
- Non-React Redux
Critical Patterns
✅ REQUIRED: Use createSlice for Reducers
// ✅ CORRECT: createSlice with immer
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1; // Immer handles immutability
},
},
});
// ❌ WRONG: Manual action types and reducers
const INCREMENT = "counter/increment";
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case INCREMENT:
return { ...state, value: state.value + 1 };
}
}
✅ REQUIRED: Use Typed Hooks
// ✅ CORRECT: Typed hooks
import { useAppDispatch, useAppSelector } from "./store/hooks";
const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();
// ❌ WRONG: Untyped hooks (no type safety)
import { useDispatch, useSelector } from "react-redux";
const count = useSelector((state: any) => state.counter.value);
✅ REQUIRED: Use configureStore
// ✅ CORRECT: configureStore with automatic middleware
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
// ❌ WRONG: Manual store setup
const store = createStore(
combineReducers({
/* ... */
}),
);
Conventions
Redux Toolkit Specific
- createSlice for reducers/actions
- Typed hooks (useAppDispatch, useAppSelector)
- createAsyncThunk for async
- Immer for immutable updates
- Follow Redux style guide
Decision Tree
Setting up Redux?
→ MUST read typescript-integration.md for store setup, typed hooks (useAppDispatch, useAppSelector), RootState/AppDispatch types
Creating slice?
→ MUST read slices-patterns.md for createSlice, reducers, extraReducers, immer patterns, prepare callbacks
Need global state?
→ Create slice with createSlice, define initial state and reducers. Use typed hooks useAppSelector/useAppDispatch
Async operation (API call)?
→ Use RTK Query for data fetching (preferred). MUST read rtk-query.md for createApi, queries, mutations, cache invalidation. For manual async: CHECK async-patterns.md for createAsyncThunk patterns
Derived/computed state?
→ CHECK selectors.md for createSelector (memoization), selector composition, preventing re-renders
Managing collections (users, posts, products)?
→ MUST read normalization.md for createEntityAdapter, normalized state, CRUD operations, relationships
State normalization needed?
→ Use createEntityAdapter for managing collections with IDs (automatic CRUD reducers, selectors)
Performance issue with re-renders?
→ Use granular selectors (select only needed data), React.memo() on components, shallowEqual in useAppSelector. CHECK selectors.md for memoization patterns
Cross-slice logic?
→ Use extraReducers in slice or dispatch actions from async thunks. Avoid direct slice imports (circular deps)
DevTools not working?
→ Verify configureStore enables DevTools by default. Use Redux DevTools Extension for time-travel debugging
Example
import { createSlice, PayloadAction, configureStore } from "@reduxjs/toolkit";
interface CounterState {
value: number;
}
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 } as CounterState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
Edge Cases
Circular deps: Avoid importing slices; use middleware/thunks for cross-slice.
Serialization: No functions/promises in state; use middleware.
Large updates: Combine actions or batch from react-redux.
Middleware order: Custom after thunk, before serializableCheck.
EntityAdapter: sortComparer for ordering; updates re-sort auto.
Hot reload: module.hot preserves state during dev reloads.
Advanced Architecture Integration
⚠️ Context Check: Apply only when:
- AGENTS.md specifies architecture
- Codebase has domain/application/infrastructure folders
- User requests patterns
If none → Use Redux Toolkit practices, skip architecture.
Applicable Patterns
- SRP: One slice per domain (user, order - not appSlice)
- Clean Architecture: RTK Query as Infrastructure
- Result Pattern: Wrap mutations/thunks with Result
Complete Guide
See frontend-integration.md for:
- Redux Toolkit + Clean Architecture
- SRP for slices
- RTK Query as Infrastructure
- Result Pattern with mutations
See architecture-patterns SKILL.md for selection.