React + Redux Toolkit
Modern Redux with RTK, slices, and RTK Query for data fetching.
# CLAUDE.md — React + Redux Toolkit
## When to use Redux
- Use Redux Toolkit (RTK) when state is shared across many disconnected parts of the app, or when actions cause coordinated changes.
- For server cache, use RTK Query (or TanStack Query). Don't keep server data in slices unless you have a specific reason.
- For local component state, use `useState`/`useReducer`. Don't promote everything to global.
## Slices
- One slice per domain. File: `src/features/<domain>/<domain>Slice.ts`.
- Use `createSlice`. Reducers are written as if mutating — Immer handles immutability.
- Co-locate selectors in the same file. Export `selectX` functions; never read state shape from components.
- Action creators are auto-generated. Don't write them by hand.
## Store
- Define the store once in `src/app/store.ts`:
```ts
export const store = configureStore({ reducer: { users: usersReducer, ... } })
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
```
- Wrap `useDispatch` and `useSelector` once with the typed versions:
```ts
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
```
- Components import the typed hooks, never the raw `useDispatch`.
## Async with createAsyncThunk
- Use `createAsyncThunk` only for actions that touch async work *and* need to be tracked in slice state (loading flag, error).
- For simple data fetching, prefer RTK Query.
- Three reducers per thunk: `pending`, `fulfilled`, `rejected`. Always handle `rejected` — never silently swallow.
## RTK Query
- One `api` slice per backend. `createApi({ baseQuery, endpoints })`.
- Tag-based invalidation: each query provides tags, each mutation invalidates them. Don't manually `refetch`.
- Co-locate endpoints with the feature. Re-export hooks from a feature index file.
## Selectors
- Use `createSelector` (Reselect, re-exported by RTK) for derived data.
- Inputs are simple state-getter selectors; the result function does the computation.
- Don't pass new objects/arrays through `useSelector` without `createSelector` — you'll re-render every state change.
## Don't
- Don't mutate state outside slice reducers. Immer only works inside `createSlice`.
- Don't dispatch from inside reducers. Reducers are pure.
- Don't put non-serializable values (Maps, Sets, class instances, Promises) in state.
- Don't store the same data in two slices. Pick the owner; let others read.
Other React templates
Next.js App Router + TypeScript Rules
Server Components, Server Actions, and TypeScript discipline for the Next.js App Router.
Next.js + Server Actions + Shadcn UI
Forms with Server Actions, Zod validation, and Shadcn UI primitives.
React + TypeScript + Vite SPA Rules
Modern React SPA conventions: hooks, Suspense, error boundaries, Vite tuning.
React Hooks Best Practices
Disciplined hook usage: deps arrays, custom hooks, refs, and effect minimalism.
React + TanStack Query
Server state with TanStack Query: caching, mutations, optimistic updates.