Redux Cheat Sheet
Redux Toolkit (RTK) is the standard way to manage global state: slices define reducers and actions, configureStore wires middleware, and hooks connect React components.
How to use this Redux cheat sheet createSlice generates action creators and Immer-powered reducers from a name, initialState, and reducers object. configureStore sets up the store with Redux DevTools and default middleware including thunk support via createAsyncThunk.
useSelector reads state with memoized selectors; useDispatch sends actions. RTK Query adds data-fetching slices with caching, invalidation tags, and generated hooks—replacing much manual thunk boilerplate.
Quick Redux example import { configureStore, createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchTodos = createAsyncThunk('todos/fetch', async () => {
const res = await fetch('/api/todos');
return res.json();
});
const todosSlice = createSlice({
name: 'todos',
initialState: { items: [], status: 'idle' },
reducers: {
added(state, action) { state.items.push(action.payload); },
},
extraReducers: (builder) => {
builder
.addCase(fetchTodos.pending, (state) => { state.status = 'loading'; })
.addCase(fetchTodos.fulfilled, (state, action) => {
state.status = 'idle';
state.items = action.payload;
});
},
});
export const store = configureStore({ reducer: { todos: todosSlice.reducer } });
export type RootState = ReturnType<typeof store.getState>; createSlice & configureStore API Example Notes createSlice createSlice({ name, initialState, reducers }) Auto-generates actions and reducer reducers increment(state) { state.value += 1; } Immer allows "mutating" syntax actions export todosSlice.actions.added Action creators with type prefix configureStore configureStore({ reducer: rootReducer }) DevTools + thunk middleware by default combineReducers { auth: authReducer, ui: uiReducer } Pass object map to reducer key preloadedState preloadedState: { auth: { user: null } } Hydrate store from SSR or localStorage middleware middleware: (gDM) => gDM().concat(logger) Extend default middleware chain RootState type ReturnType<typeof store.getState> Typed selector input
React Hooks Hook Example Purpose useSelector const user = useSelector(s => s.auth.user) Subscribe to store slice useDispatch const dispatch = useDispatch() Dispatch actions or thunks typed hooks useAppSelector, useAppDispatch Wrap with RootState/AppDispatch types shallowEqual useSelector(fn, shallowEqual) Avoid re-render when object fields unchanged dispatch action dispatch(todosSlice.actions.added(item)) Sync state update dispatch thunk dispatch(fetchTodos()) Async side effect via createAsyncThunk Provider <Provider store={store}> Make store available to React tree batch updates React 18 auto-batches Multiple dispatches in one event tick batch re-renders
createAsyncThunk Phase Example Behavior Define createAsyncThunk("users/fetch", async (id) => api(id)) Generates pending/fulfilled/rejected types pending .addCase(fetch.pending, ...) Set loading flag fulfilled .addCase(fetch.fulfilled, (s, a) => { s.data = a.payload; }) Handle success payload rejected .addCase(fetch.rejected, (s, a) => { s.error = a.error; }) Handle failure; check a.meta.rejectedWithValue condition { condition: (_, { getState }) => !loading } Skip duplicate in-flight requests rejectWithValue return rejectWithValue(err.body) Pass serializable error data to reducer unwrap await dispatch(fetch()).unwrap() Promise that throws on rejection in components extraReducers builder builder.addCase(...) Type-safe case handling vs switch
RTK Query & Selectors API Example Notes createApi createApi({ reducerPath, baseQuery, endpoints }) Define data-fetching slice baseQuery fetchBaseQuery({ baseUrl: "/api" }) HTTP wrapper with auth headers endpoint query getPosts: builder.query({ query: () => "posts" }) Generates useGetPostsQuery hook endpoint mutation addPost: builder.mutation({ query: (body) => ({ url: "posts", method: "POST", body }) }) POST/PUT/DELETE hooks tags providesTags: ["Post"], invalidatesTags: ["Post"] Cache invalidation on mutations createSelector createSelector([selectItems], items => items.filter(...)) Memoized derived state from reselect injectEndpoints api.injectEndpoints({ endpoints: ... }) Code-split API definitions setupListeners setupListeners(store.dispatch) Refetch on focus/reconnect
RTK Query API slice stub export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['Todo'],
endpoints: (builder) => ({
getTodos: builder.query<Todo[], void>({
query: () => 'todos',
providesTags: ['Todo'],
}),
addTodo: builder.mutation<Todo, Partial<Todo>>({
query: (body) => ({ url: 'todos', method: 'POST', body }),
invalidatesTags: ['Todo'],
}),
}),
});
export const { useGetTodosQuery, useAddTodoMutation } = api; Add api.reducer and api.middleware to configureStore; wrap app with RTK Query hooks.
Memoized selector const selectTodos = (state: RootState) => state.todos.items;
export const selectActiveTodos = createSelector(
[selectTodos],
(todos) => todos.filter((t) => !t.completed)
); createSelector recomputes only when input selectors return new references.
Typed React-Redux hooks import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; Export typed hooks once from store.ts and use everywhere instead of raw hooks.
Common mistakes Storing derived or denormalized data in Redux instead of computing with selectors. Mutating state outside Immer reducers (spreading nested objects manually everywhere). Putting non-serializable values (Dates, class instances, promises) in the store. Fetching in useEffect when RTK Query would handle cache, loading, and errors. Key takeaways createSlice is the default for reducers; avoid hand-written switch statements. createAsyncThunk standardizes async pending/fulfilled/rejected handling. RTK Query replaces most manual fetch-thunk-cache logic for server state. Memoized selectors prevent unnecessary re-renders in connected components.
Pro Tip
Keep server state in RTK Query and UI/ephemeral state in slices—mixing both in one mega-slice leads to tangled reducers.
Common Redux Mistakes Go to next item