[{"data":1,"prerenderedAt":126},["ShallowReactive",2],{"qa-\u002Freact\u002Fstate-management\u002Fredux-toolkit":3},{"page":4,"siblings":110,"blog":123},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":101,"related":102,"seo":103,"seoDescription":104,"stem":105,"subtopic":6,"topic":106,"topicSlug":107,"updated":108,"__hash__":109},"qa\u002Freact\u002Fstate-management\u002Fredux-toolkit.md","Redux Toolkit",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{},true,1,"\u002Freact\u002Fstate-management\u002Fredux-toolkit",[23,28,32,36,40,44,48,52,56,61,65,69,73,77,81,85,89,93,97],{"id":24,"difficulty":25,"q":26,"a":27},"what-problem-rtk-solves","easy","What problems does Redux Toolkit solve compared to plain Redux?","Plain Redux requires a lot of **boilerplate**: action type constants, action\ncreator functions, hand-written immutable updates in reducers, and manual\nmiddleware wiring. Every new feature duplicates this ceremony, leading to\nsprawling files and easy mistakes (e.g., accidentally mutating state).\n\n**Redux Toolkit (RTK)** eliminates this with three design decisions: it\nbundles **Immer** so you write mutating-looking reducer code that is\nactually immutable under the hood; it provides **`createSlice`** which\nauto-generates action creators and action types from a reducer map; and\n**`configureStore`** sets up Redux DevTools and `redux-thunk` middleware\nautomatically.\n\n```js\n\u002F\u002F Plain Redux — three files of ceremony for one counter\nconst INCREMENT = 'counter\u002Fincrement'\nconst increment = () => ({ type: INCREMENT })\nfunction reducer(state = { value: 0 }, action) {\n  switch (action.type) {\n    case INCREMENT: return { ...state, value: state.value + 1 } \u002F\u002F manual spread\n    default: return state\n  }\n}\n\n\u002F\u002F RTK — everything above in ~5 lines\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) { state.value++ } \u002F\u002F Immer handles immutability\n  }\n})\n```\n\n**Rule of thumb:** If you're writing `...state` spreads or a `types.js`\nconstants file, you should be using Redux Toolkit instead.\n",{"id":29,"difficulty":25,"q":30,"a":31},"configure-store-setup","How do you set up a Redux store with configureStore?","**`configureStore`** is RTK's store factory. It accepts a `reducer` map\n(each key becomes a top-level slice of state), and it automatically\nenables the **Redux DevTools Extension** and adds **`redux-thunk`**\nmiddleware — no manual `compose` or `applyMiddleware` needed.\n\n```js\nimport { configureStore } from '@reduxjs\u002Ftoolkit'\nimport counterReducer from '.\u002Ffeatures\u002Fcounter\u002FcounterSlice'\nimport userReducer from '.\u002Ffeatures\u002Fuser\u002FuserSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer, \u002F\u002F state.counter\n    user: userReducer,       \u002F\u002F state.user\n  },\n  \u002F\u002F middleware and devTools are auto-configured; override only when needed\n})\n\n\u002F\u002F Infer RootState and AppDispatch types (TypeScript)\nexport type RootState = ReturnType\u003Ctypeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n```\n\nWrap your app in `\u003CProvider store={store}>` from `react-redux` so every\ncomponent can access the store via hooks.\n\n**Rule of thumb:** One `configureStore` call per app; export `RootState`\nand `AppDispatch` from the same file so TypeScript consumers stay in sync.\n",{"id":33,"difficulty":25,"q":34,"a":35},"create-slice-basics","What does createSlice do and what does it return?","**`createSlice`** takes a name, an initial state, and a `reducers` object.\nIt returns a **slice object** containing the generated reducer function and\nan `actions` object whose keys match the reducer names.\n\n```js\nimport { createSlice } from '@reduxjs\u002Ftoolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',            \u002F\u002F prefix for action types: 'counter\u002Fincrement'\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) { state.value++ },           \u002F\u002F action: counterSlice.actions.increment\n    decrement(state) { state.value-- },\n    incrementBy(state, action) {\n      state.value += action.payload               \u002F\u002F payload is the argument passed to the action creator\n    },\n  },\n})\n\nexport const { increment, decrement, incrementBy } = counterSlice.actions\nexport default counterSlice.reducer               \u002F\u002F plug into configureStore\n```\n\nThe action type strings are auto-namespaced as `\"sliceName\u002FreducerName\"`,\nwhich keeps them unique across a large app without a constants file.\n\n**Rule of thumb:** One `createSlice` per feature; export named action\ncreators and the default reducer from the same file.\n",{"id":37,"difficulty":25,"q":38,"a":39},"immer-mutations","How does Immer make Redux reducers easier to write?","RTK's reducers run inside **Immer's `produce`** function. Immer gives you\na **draft proxy** of the current state. Any mutations you apply to the\ndraft are recorded and used to create a new immutable state object —\nyour original state is never changed.\n\n```js\nreducers: {\n  addTodo(state, action) {\n    state.items.push(action.payload)  \u002F\u002F looks like mutation, but Immer produces a new array\n  },\n  removeTodo(state, action) {\n    const index = state.items.findIndex(t => t.id === action.payload)\n    if (index !== -1) state.items.splice(index, 1) \u002F\u002F splice on the draft is safe\n  },\n  updateTodo(state, action) {\n    const todo = state.items.find(t => t.id === action.payload.id)\n    if (todo) todo.text = action.payload.text     \u002F\u002F direct property assignment is fine\n  },\n}\n```\n\nThe one rule: you must either **mutate the draft** OR **return a new\nvalue** — never both in the same reducer case. Returning `undefined`\nimplicitly keeps the current draft.\n\n**Rule of thumb:** Mutate the draft for nested updates; return a\nreplacement value only when you want to replace the entire state slice.\n",{"id":41,"difficulty":25,"q":42,"a":43},"use-selector-use-dispatch","How do useSelector and useDispatch connect components to the Redux store?","**`useSelector`** reads a value from the Redux store. It accepts a\nselector function that receives the full `RootState` and returns the\nvalue you need. The component re-renders only when that value changes.\n**`useDispatch`** returns the store's dispatch function so you can send\nactions.\n\n```jsx\nimport { useSelector, useDispatch } from 'react-redux'\nimport { increment, incrementBy } from '.\u002FcounterSlice'\n\nfunction Counter() {\n  const count = useSelector(state => state.counter.value) \u002F\u002F subscribe to one slice\n  const dispatch = useDispatch()\n\n  return (\n    \u003Cdiv>\n      \u003Cp>{count}\u003C\u002Fp>\n      \u003Cbutton onClick={() => dispatch(increment())}>+1\u003C\u002Fbutton>\n      \u003Cbutton onClick={() => dispatch(incrementBy(5))}>+5\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nIn TypeScript, use typed wrappers (`useAppSelector`, `useAppDispatch`)\nderived from `RootState` and `AppDispatch` so you get full autocomplete\nwithout repeating type annotations in every component.\n\n**Rule of thumb:** Keep selector functions simple in the component; move\ncomplex derived logic to standalone selector functions or `createSelector`.\n",{"id":45,"difficulty":14,"q":46,"a":47},"create-async-thunk","How does createAsyncThunk handle asynchronous actions?","**`createAsyncThunk`** wraps an async function and automatically dispatches\nthree lifecycle actions: `pending`, `fulfilled`, and `rejected`. You handle\nthese in `extraReducers` to update loading\u002Ferror\u002Fdata state.\n\n```js\nimport { createAsyncThunk, createSlice } from '@reduxjs\u002Ftoolkit'\n\n\u002F\u002F First arg: action type prefix; second arg: async payload creator\nexport const fetchUser = createAsyncThunk('user\u002FfetchById', async (userId) => {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${userId}`)\n  if (!res.ok) throw new Error('Not found')   \u002F\u002F throw to trigger rejected\n  return res.json()                             \u002F\u002F returned value becomes action.payload in fulfilled\n})\n\nconst userSlice = createSlice({\n  name: 'user',\n  initialState: { data: null, status: 'idle', error: null },\n  reducers: {},\n  extraReducers(builder) {\n    builder\n      .addCase(fetchUser.pending,   (state) => { state.status = 'loading' })\n      .addCase(fetchUser.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.data = action.payload             \u002F\u002F Immer draft mutation\n      })\n      .addCase(fetchUser.rejected,  (state, action) => {\n        state.status = 'failed'\n        state.error = action.error.message\n      })\n  },\n})\n```\n\nDispatch it like any action: `dispatch(fetchUser(42))`. The thunk returns\na promise you can `await` and `.unwrap()` to re-throw errors into a\ntry\u002Fcatch in the component.\n\n**Rule of thumb:** Use `createAsyncThunk` for one-off fetches; switch to\nRTK Query when you need caching, deduplication, or automatic refetching.\n",{"id":49,"difficulty":14,"q":50,"a":51},"loading-error-state-pattern","What is the standard pattern for tracking loading and error state with createAsyncThunk?","The convention is to keep a **`status`** field (`'idle' | 'loading' |\n'succeeded' | 'failed'`) alongside an **`error`** field in the slice's\ninitial state. Each lifecycle case updates these fields atomically with\nthe data.\n\n```js\nconst initialState = {\n  items: [],\n  status: 'idle',    \u002F\u002F drives skeleton\u002Fspinner UI\n  error: null,       \u002F\u002F drives error banner UI\n}\n\nextraReducers(builder) {\n  builder\n    .addCase(fetchItems.pending, (state) => {\n      state.status = 'loading'\n      state.error = null              \u002F\u002F clear previous error on retry\n    })\n    .addCase(fetchItems.fulfilled, (state, action) => {\n      state.status = 'succeeded'\n      state.items = action.payload\n    })\n    .addCase(fetchItems.rejected, (state, action) => {\n      state.status = 'failed'\n      state.error = action.error.message ?? 'Unknown error'\n    })\n}\n```\n\nIn the component, read `status` with `useSelector` and branch:\n`if (status === 'loading') return \u003CSpinner \u002F>`. Reset `status` to `'idle'`\nwhen navigating away so the next mount triggers a fresh fetch.\n\n**Rule of thumb:** Never use a boolean `isLoading` flag — a string `status`\nfield handles all four states without impossible combinations.\n",{"id":53,"difficulty":14,"q":54,"a":55},"rtk-query-overview","What is RTK Query and how does it differ from createAsyncThunk?","**RTK Query** is a data-fetching and caching layer built into RTK. It\ngenerates hooks, manages a normalized request cache, handles\ndeduplication, and supports automatic background refetching — things you\nwould have to build manually with `createAsyncThunk`.\n\n```js\nimport { createApi, fetchBaseQuery } from '@reduxjs\u002Ftoolkit\u002Fquery\u002Freact'\n\nexport const postsApi = createApi({\n  reducerPath: 'postsApi',              \u002F\u002F key in the Redux state\n  baseQuery: fetchBaseQuery({ baseUrl: '\u002Fapi' }),\n  endpoints: (builder) => ({\n    getPosts: builder.query({\n      query: () => '\u002Fposts',            \u002F\u002F appended to baseUrl\n    }),\n    getPostById: builder.query({\n      query: (id) => `\u002Fposts\u002F${id}`,\n    }),\n  }),\n})\n\n\u002F\u002F Auto-generated hooks follow the pattern: use\u003CEndpointName>Query\nexport const { useGetPostsQuery, useGetPostByIdQuery } = postsApi\n```\n\nAdd `postsApi.reducer` to `configureStore` and\n`postsApi.middleware` to the middleware chain. The hooks return\n`{ data, isLoading, isError }` — no manual `extraReducers` needed.\n\n**Rule of thumb:** Default to RTK Query for server data; use\n`createAsyncThunk` only for non-idempotent mutations that don't fit a\nREST endpoint pattern.\n",{"id":57,"difficulty":58,"q":59,"a":60},"rtk-query-cache-invalidation","hard","How does RTK Query handle cache invalidation with tags?","RTK Query uses a **tag** system to express which queries are invalidated\nwhen a mutation runs. A query **provides** tags; a mutation **invalidates**\nthose tags, causing every matching query to refetch.\n\n```js\nexport const postsApi = createApi({\n  reducerPath: 'postsApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '\u002Fapi' }),\n  tagTypes: ['Post'],                          \u002F\u002F register tag names\n  endpoints: (builder) => ({\n    getPosts: builder.query({\n      query: () => '\u002Fposts',\n      providesTags: ['Post'],                  \u002F\u002F this cache entry is tagged 'Post'\n    }),\n    getPostById: builder.query({\n      query: (id) => `\u002Fposts\u002F${id}`,\n      providesTags: (result, error, id) => [{ type: 'Post', id }], \u002F\u002F fine-grained tag\n    }),\n    createPost: builder.mutation({\n      query: (body) => ({ url: '\u002Fposts', method: 'POST', body }),\n      invalidatesTags: ['Post'],               \u002F\u002F bust all 'Post' queries on success\n    }),\n    updatePost: builder.mutation({\n      query: ({ id, ...patch }) => ({ url: `\u002Fposts\u002F${id}`, method: 'PATCH', body: patch }),\n      invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }], \u002F\u002F only bust this post\n    }),\n  }),\n})\n```\n\nWhen `updatePost` succeeds, only the `getPostById` query for that specific\n`id` refetches, leaving other cached posts untouched.\n\n**Rule of thumb:** Use list tags (e.g., `'Post'`) to bust the whole\ncollection; use entity tags (`{ type: 'Post', id }`) to bust individual\nitems — mix both in `createPost` to refresh the list and the detail.\n",{"id":62,"difficulty":14,"q":63,"a":64},"rtk-query-hooks-usage","How do you use RTK Query hooks in a component?","The auto-generated **`useGetXQuery`** hooks subscribe the component to the\ncached data. They return a result object with `data`, `isLoading`,\n`isFetching`, `isError`, and `error` properties.\n\n```jsx\nimport { useGetPostsQuery } from '.\u002FpostsApi'\n\nfunction PostsList() {\n  const {\n    data: posts = [],   \u002F\u002F default to [] to avoid null checks on first render\n    isLoading,\n    isError,\n    error,\n    refetch,            \u002F\u002F manually trigger a refetch\n  } = useGetPostsQuery()\n\n  if (isLoading) return \u003Cp>Loading…\u003C\u002Fp>\n  if (isError)   return \u003Cp>Error: {error.message}\u003C\u002Fp>\n\n  return (\n    \u003Cul>\n      {posts.map(post => \u003Cli key={post.id}>{post.title}\u003C\u002Fli>)}\n    \u003C\u002Ful>\n  )\n}\n```\n\nFor mutations, use the auto-generated `useXMutation` hook which returns\na `[trigger, result]` tuple — call `trigger(args)` to fire the request.\n\n**Rule of thumb:** Prefer `isLoading` (true only on first fetch) over\n`isFetching` (true on every fetch including background refetches) when\nrendering the initial skeleton.\n",{"id":66,"difficulty":25,"q":67,"a":68},"redux-devtools","How does Redux DevTools work with RTK and what can you inspect?","**Redux DevTools** is a browser extension that `configureStore` enables\nautomatically in development (no extra config needed). It connects to the\nstore and records every action dispatched along with a before\u002Fafter state\nsnapshot.\n\n```js\n\u002F\u002F configureStore enables DevTools automatically; disable in prod if needed\nexport const store = configureStore({\n  reducer: rootReducer,\n  devTools: process.env.NODE_ENV !== 'production', \u002F\u002F explicit control\n})\n```\n\nWith the extension open you can: **time-travel** (jump to any past state),\n**replay** the action log after a hot reload, **inspect** the diff between\nstates, and **dispatch** actions manually from the DevTools panel to test\nreducers without touching the UI.\n\nRTK also serializes the `createAsyncThunk` lifecycle actions so you see\n`user\u002FfetchById\u002Fpending`, `user\u002FfetchById\u002Ffulfilled`, etc., in the log.\n\n**Rule of thumb:** Keep action payloads serializable (plain objects, no\nclass instances, no functions) so DevTools can display and replay them\naccurately.\n",{"id":70,"difficulty":58,"q":71,"a":72},"create-entity-adapter","What is createEntityAdapter and when should you use it?","**`createEntityAdapter`** provides a standardized way to store a\ncollection of items by their ID. It creates an **`{ ids: [], entities: {} }`**\nnormalized shape and generates CRUD reducer helpers (`addOne`, `upsertMany`,\n`removeOne`, etc.).\n\n```js\nimport { createEntityAdapter, createSlice } from '@reduxjs\u002Ftoolkit'\n\nconst todosAdapter = createEntityAdapter()\n\u002F\u002F { selectAll, selectById, selectIds, selectEntities, selectTotal }\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: todosAdapter.getInitialState({ status: 'idle' }), \u002F\u002F adds ids\u002Fentities keys\n  reducers: {\n    todoAdded:   todosAdapter.addOne,         \u002F\u002F pre-built reducer\n    todosLoaded: todosAdapter.setAll,\n    todoRemoved: todosAdapter.removeOne,\n  },\n  extraReducers(builder) {\n    builder.addCase(fetchTodos.fulfilled, (state, action) => {\n      todosAdapter.setAll(state, action.payload) \u002F\u002F bulk replace\n    })\n  },\n})\n\n\u002F\u002F Selectors are pre-built and accept RootState\nexport const { selectAll: selectAllTodos, selectById: selectTodoById } =\n  todosAdapter.getSelectors(state => state.todos)\n```\n\nNormalize when you have a large list and need O(1) lookups by ID (e.g.,\nselecting one item without scanning an array).\n\n**Rule of thumb:** Use `createEntityAdapter` whenever you fetch a list\nthat you later update by ID; skip it for small static lists.\n",{"id":74,"difficulty":14,"q":75,"a":76},"memoized-selectors","How do you create memoized selectors with createSelector?","**`createSelector`** (re-exported from `reselect` by RTK) takes one or\nmore **input selectors** and a **result function**. The result is\nrecomputed only when an input selector's output changes, preventing\nunnecessary re-renders from derived computations.\n\n```js\nimport { createSelector } from '@reduxjs\u002Ftoolkit'\n\nconst selectTodos = state => state.todos.items          \u002F\u002F input selector\nconst selectFilter = state => state.todos.filter        \u002F\u002F input selector\n\n\u002F\u002F result function only runs when selectTodos or selectFilter changes\nexport const selectFilteredTodos = createSelector(\n  [selectTodos, selectFilter],\n  (todos, filter) => {\n    if (filter === 'all') return todos\n    return todos.filter(t => t.status === filter)       \u002F\u002F expensive work cached\n  }\n)\n```\n\nUse it inside `useSelector`: `const todos = useSelector(selectFilteredTodos)`.\nBecause the selector reference is stable across renders (the memoized\ninstance is defined outside the component), React-Redux's equality check\nworks correctly.\n\n**Rule of thumb:** Extract a `createSelector` call whenever the selector\ncomputes a new array or object reference — otherwise every call returns a\nnew reference and causes a re-render even when the data hasn't changed.\n",{"id":78,"difficulty":14,"q":79,"a":80},"middleware-thunk-custom","How do you add custom middleware to a Redux Toolkit store?","`configureStore` adds `redux-thunk` by default. To add custom middleware,\nuse the **`middleware`** callback option which receives `getDefaultMiddleware`\nso you can keep the defaults and append your own.\n\n```js\nimport { configureStore } from '@reduxjs\u002Ftoolkit'\n\nconst loggerMiddleware = store => next => action => {\n  console.log('dispatching', action)       \u002F\u002F called before reducer\n  const result = next(action)              \u002F\u002F pass action down the chain\n  console.log('next state', store.getState())\n  return result\n}\n\nexport const store = configureStore({\n  reducer: rootReducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware()               \u002F\u002F keeps thunk + serializability checks\n      .concat(loggerMiddleware),         \u002F\u002F append custom middleware\n})\n```\n\nDo not replace `getDefaultMiddleware()` with an empty array unless you\nintentionally want to remove the serialization warnings and thunk support —\nthose defaults catch common bugs.\n\n**Rule of thumb:** Always call `getDefaultMiddleware()` as the base;\n`prepend` for middleware that needs to run before thunks, `concat` for\neverything else.\n",{"id":82,"difficulty":14,"q":83,"a":84},"feature-folder-structure","What is the recommended way to structure Redux slices in a large app?","The **feature folder** (or \"ducks\") pattern co-locates everything related\nto one domain: the slice, thunks, selectors, and component files. This\nkeeps related code together and avoids cross-cutting files like\n`allReducers.js`.\n\n```\nsrc\u002F\n  features\u002F\n    cart\u002F\n      cartSlice.ts       \u002F\u002F createSlice + extraReducers\n      cartThunks.ts      \u002F\u002F createAsyncThunk (or inline in slice)\n      cartSelectors.ts   \u002F\u002F createSelector calls\n      CartPage.tsx       \u002F\u002F component that uses the slice\n      cartApi.ts         \u002F\u002F RTK Query createApi (if feature-scoped)\n    user\u002F\n      userSlice.ts\n      userSelectors.ts\n      ProfilePage.tsx\n  store\u002F\n    store.ts             \u002F\u002F configureStore — imports slice reducers\n    hooks.ts             \u002F\u002F typed useAppSelector \u002F useAppDispatch\n```\n\n```js\n\u002F\u002F store\u002Fhooks.ts — typed wrappers to avoid repeating types in every component\nimport { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'\nimport type { RootState, AppDispatch } from '.\u002Fstore'\nexport const useAppDispatch = () => useDispatch\u003CAppDispatch>()\nexport const useAppSelector: TypedUseSelectorHook\u003CRootState> = useSelector\n```\n\n**Rule of thumb:** Keep each feature self-contained; the `store.ts` file\nshould only import reducers — no business logic lives there.\n",{"id":86,"difficulty":14,"q":87,"a":88},"when-not-to-use-redux","When should you NOT use Redux for state management?","Redux shines for **shared, cross-component global state** with complex\nupdate logic. It is overkill — and adds friction — for state that fits\nsimpler tools.\n\nAvoid Redux for:\n- **Local UI state** (modal open, form input value, hover) — `useState` is\n  sufficient and keeps the logic next to the component.\n- **Server cache state** (fetched lists, single items) — RTK Query or React\n  Query handle caching, deduplication, and refetching better than a\n  hand-rolled async slice.\n- **Small apps with one team** — Context + `useReducer` may be all you\n  need; the cognitive overhead of slices\u002Fselectors\u002Factions is not always\n  worth it.\n\n```jsx\n\u002F\u002F BAD — Redux for toggle state no other component needs\ndispatch(setModalOpen(true))\n\n\u002F\u002F GOOD — local state for isolated UI\nconst [isOpen, setIsOpen] = useState(false)\n```\n\nSymptoms of unnecessary Redux: your slices hold `isModalOpen` flags,\nyou dispatch actions from a single component and no other component\nsubscribes, or you have more action types than actual data fields.\n\n**Rule of thumb:** If no other component needs to read or write a piece\nof state, keep it local with `useState` — move it to Redux only when the\nneed actually arises.\n",{"id":90,"difficulty":58,"q":91,"a":92},"prepare-callback","What is the prepare callback in createSlice and when do you use it?","By default, action creators generated by `createSlice` accept one\nargument which becomes `action.payload`. The **`prepare` callback** lets\nyou transform the arguments — generate IDs, add timestamps, or restructure\nthe payload — before the reducer runs.\n\n```js\nimport { createSlice, nanoid } from '@reduxjs\u002Ftoolkit'\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [],\n  reducers: {\n    todoAdded: {\n      reducer(state, action) {\n        state.push(action.payload)                \u002F\u002F payload already shaped\n      },\n      prepare(text) {                            \u002F\u002F called first with the caller's args\n        return {\n          payload: {\n            id: nanoid(),                        \u002F\u002F auto-generate a unique id\n            text,\n            createdAt: new Date().toISOString(), \u002F\u002F timestamp at dispatch time\n            completed: false,\n          },\n        }\n      },\n    },\n  },\n})\n\n\u002F\u002F Caller just passes the text — ID and timestamp are added automatically\ndispatch(todoAdded('Buy milk'))\n```\n\n**Rule of thumb:** Use `prepare` to keep side effects (ID generation,\ntimestamps) out of reducers and out of components — reducers must be\npure, and components shouldn't be responsible for shaping payloads.\n",{"id":94,"difficulty":14,"q":95,"a":96},"rtk-query-mutation","How do you perform a mutation with RTK Query and update the UI after it completes?","Mutations are defined with `builder.mutation` and exposed as\n**`useXMutation`** hooks. The hook returns a `[triggerFn, resultObject]`\ntuple. Call the trigger to fire the request; the result object tracks\nthe mutation's lifecycle.\n\n```jsx\nimport { useCreatePostMutation } from '.\u002FpostsApi'\n\nfunction NewPostForm() {\n  const [createPost, { isLoading, isError, isSuccess }] = useCreatePostMutation()\n  const [title, setTitle] = useState('')\n\n  async function handleSubmit(e) {\n    e.preventDefault()\n    try {\n      await createPost({ title }).unwrap() \u002F\u002F unwrap re-throws on error\n      setTitle('')                          \u002F\u002F clear form on success\n    } catch (err) {\n      console.error('Failed to save post', err)\n    }\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput value={title} onChange={e => setTitle(e.target.value)} \u002F>\n      \u003Cbutton disabled={isLoading}>\n        {isLoading ? 'Saving…' : 'Save'}\n      \u003C\u002Fbutton>\n      {isSuccess && \u003Cp>Saved!\u003C\u002Fp>}\n      {isError   && \u003Cp>Something went wrong.\u003C\u002Fp>}\n    \u003C\u002Fform>\n  )\n}\n```\n\nIf the mutation's `invalidatesTags` overlaps with a query's `providesTags`,\nthat query automatically refetches — no manual cache update needed.\n\n**Rule of thumb:** Always call `.unwrap()` in a try\u002Fcatch so thrown errors\nsurface in your component rather than being silently swallowed.\n",{"id":98,"difficulty":25,"q":99,"a":100},"action-type-strings","How are Redux action type strings structured in Redux Toolkit?","RTK automatically names action types as **`\"sliceName\u002FreducerName\"`** for\nslice actions, and **`\"actionPrefix\u002Fpending|fulfilled|rejected\"`** for\n`createAsyncThunk` lifecycle actions.\n\n```js\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) { state.value++ },\n  },\n})\n\ncounterSlice.actions.increment.type   \u002F\u002F 'counter\u002Fincrement'\ncounterSlice.actions.increment()      \u002F\u002F { type: 'counter\u002Fincrement' }\n\nconst fetchUser = createAsyncThunk('user\u002Ffetch', async (id) => { \u002F* ... *\u002F })\nfetchUser.pending.type    \u002F\u002F 'user\u002Ffetch\u002Fpending'\nfetchUser.fulfilled.type  \u002F\u002F 'user\u002Ffetch\u002Ffulfilled'\nfetchUser.rejected.type   \u002F\u002F 'user\u002Ffetch\u002Frejected'\n```\n\nBecause names are derived from the slice name, keep slice names\ndescriptive and scoped to their feature. Avoid generic names like\n`'data'` or `'state'` which will produce cryptic action logs in DevTools.\n\n**Rule of thumb:** The DevTools action log is your first debugging tool —\ntreat action type strings as documentation and name them clearly.\n",19,null,{"description":11},"Redux Toolkit interview questions — createSlice, configureStore, createAsyncThunk, RTK Query, immer mutations, and Redux DevTools for React state management.","react\u002Fstate-management\u002Fredux-toolkit","State Management","state-management","2026-06-24","McrLS2seIz5b0yfkIShk3xiUDgZ1Ep8swJGu_i4hSCw",[111,112,115,119],{"subtopic":6,"path":21,"order":20},{"subtopic":113,"path":114,"order":12},"Zustand","\u002Freact\u002Fstate-management\u002Fzustand",{"subtopic":116,"path":117,"order":118},"Context vs Redux","\u002Freact\u002Fstate-management\u002Fcontext-vs-redux",3,{"subtopic":120,"path":121,"order":122},"Async State & React Query","\u002Freact\u002Fstate-management\u002Fasync-state-react-query",4,{"path":124,"title":125},"\u002Fblog\u002Freact-redux-toolkit-guide","Redux Toolkit in React — Complete Interview Guide",1782244101061]