skip to content

Redux

2 roadmaps30 questionsupdated

Redux keeps all application state in one immutable store, changed only by dispatching actions through pure reducers. It remains the most-asked state library in interviews, both on its own terms and as the reference point every newer library gets compared against.

on this pageshow

guide

overview

~1 min

Redux is a small library for holding application state outside the UI: one store, a plain-data state tree, changes described as actions and applied by pure reducer functions. Interviewers keep asking about it for two reasons. Many production React codebases still run on it, and its rules — explicit updates, immutable data, one direction of flow — are the vocabulary used to judge every newer state library. A good answer explains why a rule exists, not only what the code looks like. The hub follows the path of a single update. [Store and data flow](/topics/fe-redux-store) is the loop everything else plugs into. [Actions and reducers](/topics/fe-redux-actions-reducers) cover how a change is described and applied, and why immutability is a correctness rule rather than a style preference. [Middleware and thunks](/topics/fe-redux-middleware) is where async work and side effects live. [Selectors and Reselect](/topics/fe-redux-selectors) and [React integration and DevTools](/topics/fe-redux-react-integration) are the read side, where most performance questions sit. [Redux Toolkit](/topics/fe-redux-toolkit) is how Redux is written today, and interviewers want to know what it does underneath. Junior rounds check the loop and its vocabulary. Middle and senior rounds move to re-render cost, stale async responses, state shape and server rendering; principal rounds ask when Redux, or a particular async library on top of it, still earns its place. Learn the hand-written loop and middleware first, then Toolkit, then the read side, where selectors and subscriptions decide performance.

primer

### State changes are events, not assignments Components never write to the store. They dispatch an action — a plain object saying what happened — and the store works out the consequences. That indirection is both the price of Redux and its benefit: every change is visible, loggable and replayable, and a bug traces back to the action that caused it. ### Reducers are pure functions A reducer computes new state from nothing but its two arguments: no I/O, no randomness, no hidden inputs. Purity is what makes replay, testing and time-travel debugging trustworthy, and it is why async work has to live somewhere else. ### Immutability is how change is detected Redux and React-Redux decide whether something changed by comparing references, not contents. An object edited in place keeps its reference, so any code comparing that reference concludes nothing happened. An update copies every object on the path to the changed value; Immer, inside Toolkit, does the copying for you, but the rule is the same. ### Side effects live in middleware Middleware wraps dispatch and sees each action before the reducers do, which makes it the extension point for anything impure: fetching, logging, reacting to one action by dispatching others. Thunks, sagas and the Toolkit listener middleware are three answers to where async flow is written down, with different costs in readability and control. ### Reads are derived, and derivation has a cost Components read through selectors, which hide the state shape and compute derived values. Subscribed components rerun their selectors after every dispatch, so a selector that builds a fresh object or array each time causes renders nobody asked for. Memoization and narrower subscriptions are the tools; knowing which one a symptom needs is a senior-level skill. ### Toolkit is the default, not a different library Redux Toolkit keeps the same store, actions and reducers and removes the ceremony: generated action types and creators, a preconfigured store, Immer-based updates, a standard async request lifecycle. Interviewers expect you to write it and to explain the hand-written code it replaces.

Store
The object holding the current state tree, exposing dispatch, getState and subscribe. An application normally has exactly one.
Action
A plain object with a string type field describing an event that happened, usually carrying its data in a payload field.
Reducer
A pure function that takes the current state and an action and returns the next state, without mutating its inputs or causing side effects.
Dispatch
The store method that sends an action through the middleware chain to the root reducer, then notifies subscribers.
Middleware
A function wrapping dispatch that sees every action before the reducers do; the standard place for async logic, logging and other side effects.
Thunk
A function dispatched in place of an action. The thunk middleware calls it with dispatch and getState so it can run async work.
Selector
A function that takes the root state, and possibly arguments, and returns a piece of it or a value derived from it.
Memoized selector
A selector that caches its output and returns the same reference until its inputs change, usually built with Reselect's createSelector.
Slice
The part of the state tree owned by one feature reducer; in Redux Toolkit, also the object createSlice returns with that reducer and its actions.
Immer
A library that records changes made to a draft of the state and produces a new immutable state from them; Redux Toolkit uses it inside reducers.
Normalized state
A state shape that stores each entity once, keyed by id, and holds relationships as ids instead of nested copies.

Trace one update. An event handler dispatches an action, which passes through each middleware in turn; any of them may log it, delay it, swallow it or dispatch others, and a dispatched thunk stops here. A plain action arrives at the root reducer, which — usually through `combineReducers` or Toolkit's reducer map — hands each slice reducer its own part of the state. The store saves the result and notifies every subscriber. React-Redux is one such subscriber: each `useSelector` reruns its selector against the new state, compares the result with the previous one, and re-renders its component only when they differ. Every section of the hub attaches to one point on that loop: - **Actions and reducers** are the write side: how the event is described and how the next state is built. - **Middleware** sits between dispatch and the reducers. - **Selectors** and **React integration** are the read side, where the cost of each dispatch is decided. - **Toolkit** wraps every step without changing the loop. - **DevTools** records actions at the store and replays them through the reducers, which works only while the reducers stay pure. In Toolkit code the write side and the read side meet in a few lines: ```ts const inbox = createSlice({ name: 'inbox', initialState: { messages: [] as Message[] }, reducers: { markedRead(state, action: PayloadAction<string>) { const m = state.messages.find(m => m.id === action.payload) if (m) m.read = true // Immer turns this into a copied path }, }, }) const selectUnread = createSelector( [(s: RootState) => s.inbox.messages], messages => messages.filter(m => !m.read), // same array until messages change ) const unread = useSelector(selectUnread) // re-renders only on a new result ``` Neither half is enough alone: the reducer must produce a new reference for the change to be seen, and the selector must avoid producing one when nothing relevant changed.

  1. Store and Data Flow →

    The loop every other section plugs into: one store, dispatch, reducer, subscribers, and why state stays plain and serializable.

  2. Actions and Reducers →

    How a change is described and applied, and why purity and immutable updates are correctness rules rather than style.

  3. Middleware and Thunks →

    Where async work and side effects go once reducers are pure, and how thunks and sagas differ.

  4. Redux Toolkit →

    The modern way to write everything above; it makes sense once you know the boilerplate it generates.

  5. Selectors and Reselect →

    Reading and deriving state without coupling components to its shape or recomputing on every dispatch.

  6. React Integration and DevTools →

    Hooks, subscription granularity and DevTools: where selector choices turn into render counts you can measure.

  • Spreading only the top level of state and calling the update immutable: nested objects keep their old references, so an in-place edit below the root goes unnoticed.

  • Putting fetches, timers or random ids inside a reducer; side effects belong in middleware, or in the code that prepares the action's payload.

  • Writing a useSelector that builds a new object or array on every call, then wondering why the component renders after every dispatch.

  • Wrapping a selector that just returns a stored field in createSelector; memoization helps only when the output is derived and would otherwise be rebuilt.

  • In a Toolkit case reducer, both mutating the draft and returning a new value — Immer refuses that; do one or the other.

  • Keeping Promises, class instances or other non-serializable values in state, which breaks DevTools replay, persistence and server hydration.

  • Treating Redux as the home for all state: cached server data and short-lived form or UI state often belong in a data-fetching layer or component state.

  • Sharing one module-level store across server-rendered requests, which can leak one user's state into another's page — see store per request.

This guide assumes Redux 5, Redux Toolkit 2, React-Redux 9 and Reselect 5, which were released together at the end of 2023. Older lines are still common in production, and several questions turn on the difference: - **React-Redux 8** rebuilt its subscriptions on React 18's `useSyncExternalStore`; **9** requires React 18. - **Redux 5** marks the original `createStore` as deprecated in favour of Toolkit's `configureStore`. It still works, and `legacy_createStore` is the same function without the marking. - **Redux Toolkit 2** removed the object form of `extraReducers` and `createReducer`, leaving the builder callback, and requires the `middleware` option of `configureStore` to be a callback. - **Reselect 5** replaced the single-entry cache with `weakMapMemoize` as the default, which changes how one selector behaves when called with different arguments. When an answer depends on one of these — store setup, selector caching, hooks versus `connect` — say which version you are describing.

Inside its own ecosystem, thunks ship with Toolkit, redux-saga uses generator functions for cancellable, long-running flows, and RTK Query, part of Toolkit, handles data fetching and caching so server data no longer needs hand-written thunks and reducers. Outside it, interviewers expect you to place Redux against: - **React context**, which passes a value down the tree but is not a state manager: every consumer re-renders when the value changes, with no selector-level subscription. - **Zustand**, a single external store with selector subscriptions but no required actions or reducers — less ceremony, less enforced structure. - **MobX**, which tracks observable state and re-renders whatever read it, trading an explicit event log for automatic dependency tracking. - **TanStack Query**, which, like RTK Query, treats server data as a cache rather than application state, removing much of what Redux apps once held. A defensible choice names the trade: Redux buys traceability and one shared structure for a large team, and pays for it in indirection. [Redux Toolkit](/topics/fe-redux-toolkit) is where that cost is mostly paid down.

explore

report an issue with this guide →

questions

30

In Redux, what is an action, what is an action creator, and why write action creators instead of inline action objects?

level: juniorimportance: must knowfreq 72%

answer

  1. a plain object describing what happened
  2. type is required, and a string
  3. payload, meta, error
  4. a function that returns the object

basics

~20 s

An action is a plain object with a string type that describes something that happened, usually with its data in payload; an action creator is a function that builds that object, so every dispatch site produces the same, correctly shaped action.

solid answer

~40 s

An action is the only input a Redux store accepts: a plain object whose `type` names an event, such as `'todos/todoAdded'`, plus any data it needs, conventionally in `payload` (the Flux Standard Action shape also allows `meta` and `error`). In Redux 5, `dispatch` throws if the action is not a plain object or if `type` is not a string. An action creator is just a function that returns an action: `todoAdded(text)` returns `{ type: 'todos/todoAdded', payload: { id, text } }`. Creators keep the type string and payload shape in one place, give you a typed signature, and are where you prepare non-deterministic data such as generated IDs or timestamps, which a pure reducer must not create. Redux Toolkit generates them for you, but the idea is the same.

code

ts · 16 lines
ts
export const TODO_ADDED = 'todos/todoAdded'

export interface TodoAddedAction {
  type: typeof TODO_ADDED
  payload: { id: string; text: string; createdAt: number }
}

// Impure preparation (ID, clock) happens here, before dispatch
export function todoAdded(text: string): TodoAddedAction {
  return {
    type: TODO_ADDED,
    payload: { id: crypto.randomUUID(), text, createdAt: Date.now() },
  }
}

// dispatch(todoAdded('Buy milk'))

go deeper

for a junior

Define an action as a plain object with a string type and a payload, and an action creator as a function that returns one.

for a middle

Explain why impure preparation such as IDs or timestamps belongs in the creator, and what Redux 5 enforces on the type field.

for a senior

Argue for event-style actions over setters: fewer dispatches, consistent updates and a readable log, and show how that shapes reducer design.

for a principal

Treat the action log as a product artefact: naming conventions and payload contracts that make debugging, analytics and replay workable across teams.

## Actions: events, described as data In Redux, nothing changes state except an **action** passed to `dispatch`. An action is a **plain JavaScript object** that describes *something that happened* in the app: ```js { type: 'todos/todoAdded', payload: { id: 'a1', text: 'Buy milk' } } ``` The rules come from the core and from convention: - **Plain object.** The core `dispatch` throws "Actions must be plain objects" for anything else: a function, a Promise or a class instance. Middleware can accept other values, but whatever finally reaches the reducer must be a plain object. - **`type` is required and must be a string.** Redux 5 throws if `type` is `undefined` and also if it is not a string. Redux 4 accepted any defined value, such as a Symbol or a number; Redux 5 made strings mandatory so actions stay serializable and readable in the DevTools log. - **Everything else is up to you**, but the **Flux Standard Action** convention, which the Redux style guide recommends, says: put the data in `payload`, optional extra information in `meta`, and flag failures with `error`. ### Naming the type The style guide suggests `'domain/eventName'` strings such as `'todos/todoAdded'`. The type is what reducers switch on and what you read in the action history, so it should say **what happened**, not which field to set. ## Events, not setters A good action describes an event (`'cart/checkoutSubmitted'`) rather than a command to set a field (`'cart/setIsSubmitting'`). The difference matters: | Setter-style actions | Event-style actions | |---|---| | Caller must know the state shape | Caller only says what happened | | Several dispatches for one user event | One dispatch, many reducers may respond | | Log reads like assignments | Log reads like a story of the session | | Intermediate states between dispatches | One consistent update | ## Action creators An **action creator** is a function that returns an action: ```ts const todoAdded = (text: string) => ({ type: 'todos/todoAdded', payload: { id: crypto.randomUUID(), text }, }) ``` Nothing in Redux requires them; `dispatch({ type: 'todos/todoAdded', payload })` works. Teams use them because: 1. **One source of truth for the shape.** The type string and payload structure live in one function instead of being retyped at every dispatch site, where a typo in a type string silently matches no reducer. 2. **Preparation logic has a home.** Generating an ID, a timestamp or a normalized payload is **impure** work. It cannot go in the reducer, which must return the same output for the same input, so it belongs in the creator, before dispatch. 3. **Types.** A creator gives TypeScript a single place to declare the action type, so reducers and dispatch sites agree. 4. **Readability.** `dispatch(todoAdded(text))` reads as intent. Redux also ships `bindActionCreators`, which wraps creators so that calling them dispatches immediately. It is rarely needed with hooks, but it shows the idea: a creator only **builds** an action; dispatching is a separate step. ## Action creators are not thunks An action creator returns a **plain object** and has no side effects apart from preparation. A function that returns **another function** to be run by middleware is a *thunk action creator*, a different tool for async logic. Mixing the terms is a common interview slip. ## Judging actions | Action | Verdict | Why | |---|---|---| | `{ type: 'todos/todoAdded', payload: { id, text } }` | Good | Event name, data in `payload`, serializable | | `{ type: 'SET_DATA', payload }` | Legal but weak | Generic setter; the log says nothing about what happened | | `{ type: Symbol('added') }` | Throws in Redux 5 | `type` must be a string | | `{ type: 'user/loggedIn', payload: new User(json) }` | Avoid | Class instances are not serializable and may mutate in place | | `() => fetchTodos()` | Throws without middleware | Not a plain object; needs the thunk middleware | Two quick checks catch most mistakes. First, **could you log it and replay it tomorrow?** If the action holds a function, a Promise or a class instance, the answer is no. Second, **does the type read like a past-tense event?** If it reads like an assignment, consider whether one event-style action could replace several setters. ## Modern practice In Redux Toolkit, `createSlice` generates the type strings and action creators from your reducer names, and each creator exposes its `type`. The underlying object is still a plain action with `type` and `payload`, so everything above explains what the generated code does.

  • Why must a generated ID be created in the action creator rather than in the reducer?
    A reducer must be pure: the same state and action must always produce the same result. Replaying recorded actions, running tests or time-travel debugging would otherwise create different IDs each time. Generating the ID before dispatch puts it inside the action, so every replay reproduces the same state.
  • Why does the Redux style guide prefer actions that describe events over actions that set fields?
    An event such as 'cart/checkoutSubmitted' lets any number of reducers respond in one dispatch, keeps callers ignorant of the state shape, and produces a log that reads as what the user did. Setter actions push state knowledge into callers, need several dispatches per user event and create intermediate states between them.

saying these in an interview costs you the question

  • An action is a function that updates the state
  • Any value can be an action type in Redux 5, including Symbols
  • Action creators dispatch the action themselves
  • Random IDs should be generated inside the reducer
  • Action types should name the field being set, like SET_NAME
open as a page

Why can't a plain Redux store run async logic on its own, and how does redux-thunk let you dispatch an async fetch?

level: juniorimportance: must knowfreq 74%

basics

~20 s

The core dispatch accepts only plain objects and reducers must stay synchronous and pure, so async work needs middleware; redux-thunk lets you dispatch a function that receives dispatch and getState and dispatches plain actions as the request progresses.

open as a page

In React-Redux, what do Provider, useSelector and useDispatch each do when you connect a Redux store to a React app?

level: juniorimportance: must knowfreq 74%

basics

~20 s

Provider makes the Redux store available to every component beneath it. useSelector reads a value from the store and re-renders the component when that value changes. useDispatch returns the store's dispatch function so components can send actions.

open as a page

In Redux, what is a selector function, and why do components call selectors instead of reading state paths directly?

level: juniorimportance: must knowfreq 62%

basics

~20 s

A Redux selector is a function of the root state, plus optional arguments, returning part of it or a value derived from it. Using selectors keeps knowledge of the state shape in one place, so reshaping state changes selectors, not components.

open as a page

In a React app backed by Redux, what happens, step by step, from a user clicking an Add button until the screen shows the change?

level: juniorimportance: must knowfreq 75%

basics

~20 s

The click handler dispatches an action; the store runs the root reducer with the current state and that action, saves the result, then calls every subscriber, and the subscribed UI rereads state and re-renders what changed.

open as a page

In Redux Toolkit, what does createSlice generate when you convert a hand-written todos reducer into a slice?

level: juniorimportance: must knowfreq 76%

basics

~20 s

createSlice takes a name, an initialState and a reducers object and returns the slice reducer plus one action creator per case reducer, with each type generated as name/caseName, replacing hand-written constants, action creators and the switch.

open as a page

What contract must a Redux reducer honour for undefined state, unknown actions and side effects, and why does Redux depend on it?

level: middleimportance: must knowfreq 68%

basics

~20 s

A Redux reducer takes (state, action) and returns the next state: its initial state when given undefined, the same state for actions it ignores, never undefined, and without side effects, so replaying actions always reproduces the same state.

open as a page

What is the signature of a Redux middleware, and how would you write one that logs every action and the state it produces?

level: middleimportance: must knowfreq 58%

basics

~10 s

A Redux middleware is storeAPI => next => action => result; a logger reads getState(), calls next(action) to let the action reach the reducer, reads getState() again, and returns next's result.

open as a page

In React-Redux, why does useSelector(state => ({ count: state.count, user: state.user })) re-render its component after every dispatched action?

level: middleimportance: must knowfreq 70%

basics

~20 s

The selector builds a new object on every run, and useSelector compares results with === by default, so after any dispatch the result looks changed and the component re-renders. Select each field separately, or pass shallowEqual as the equality function.

open as a page

With Reselect's createSelector, what is the difference between input selectors and the result function, and when does the result function rerun?

level: middleimportance: must knowfreq 66%

basics

~20 s

Input selectors extract values from state and arguments; the result function computes the output from their results. It reruns only when the input results, compared by reference, match no cached combination; otherwise the cached output is returned.

open as a page

What does Redux Toolkit's configureStore set up by default that a bare Redux 5 createStore call leaves out?

level: middleimportance: must knowfreq 62%

basics

~10 s

configureStore combines a reducer map automatically, installs the thunk middleware, adds development-only checks for state mutation, non-serializable values and dispatched action creators, and connects the Redux DevTools extension, all without hand-written enhancer composition.

open as a page

In Redux Toolkit, which actions does one createAsyncThunk call dispatch, and how does rejectWithValue change the rejected action?

level: middleimportance: must knowfreq 63%

basics

~20 s

A createAsyncThunk dispatches typePrefix/pending before the payload creator runs, then typePrefix/fulfilled with the resolved value or typePrefix/rejected on failure. rejectWithValue puts your chosen value in the rejected action's payload instead of losing it to error serialisation.

open as a page

In a Redux Toolkit case reducer, why may you either mutate state or return a new value, but never both?

level: middleimportance: must knowfreq 64%

basics

~20 s

Redux Toolkit runs case reducers through Immer, which treats either the recorded draft mutations or the returned value as the next state. Mutating the draft and also returning a different value is ambiguous, so Immer throws.

open as a page

A Redux reducer runs `state.lists[id].items.push(item)` and returns `{ ...state }`, yet the list component never re-renders. Why, and how do you fix it?

level: seniorimportance: must knowfreq 60%

basics

~20 s

The spread copies only the root; lists, the list and its items array keep their references, and push mutated that array in place, so a selector returns the same array and the component skips rendering. Copy every level on the path.

open as a page

Why does a Redux todo list re-render every TodoItem on each dispatch when selectVisibleTodos filters state.todos, and how does Reselect fix it?

level: seniorimportance: must knowfreq 54%

basics

~20 s

A plain selector calling filter returns a new array every call, so after any dispatch the list sees a changed value and re-renders every item. createSelector returns the cached array until todos or the filter actually change.

open as a page

How does Redux's combineReducers compute the next state on each dispatch, and when does it return the previous root object unchanged?

level: middleimportance: should knowfreq 55%

basics

~10 s

combineReducers calls every slice reducer with its own slice and the same action, collects the results under the same keys, and returns the previous root object when every slice returned its previous reference.

open as a page

Why does Redux recommend storing relational data in a normalized shape with byId and allIds, and how does that change reducer code?

level: middleimportance: should knowfreq 48%

basics

~20 s

Normalizing stores each entity type once, in a byId lookup plus an allIds order array, with relations held as IDs, so each record has one copy, updates touch one short path, and unrelated components keep their references.

open as a page

In Redux's applyMiddleware chain, in what order do middlewares see an action, and how does next(action) differ from storeAPI.dispatch(action)?

level: middleimportance: should knowfreq 42%

basics

~20 s

applyMiddleware(a, b, c) makes a the outermost wrapper: actions flow a, b, c, then the reducer, and results flow back c, b, a. next(action) passes the action one step inward; storeAPI.dispatch(action) restarts it at a.

open as a page

In React-Redux 9, what does connect() do for render performance that useSelector does not, and why do most codebases still prefer hooks?

level: middleimportance: should knowfreq 42%

basics

~20 s

connect wraps the component in React.memo and shallow-compares the mapState result and incoming props, so it skips parent-driven renders. useSelector compares only its own result and never blocks parent renders, yet hooks need far less code and typing.

open as a page

How does Redux DevTools time-travel debugging work, and what in your reducers or state stops it from replaying correctly?

level: middleimportance: should knowfreq 38%

basics

~20 s

Redux DevTools records each dispatched action with the state it produced; jumping restores a recorded state, and skipping an action recomputes later states by re-running reducers. Impure reducers, mutated state and non-serialisable values make that replay wrong or impossible.

open as a page

In Redux, what does the preloadedState argument to createStore do, and how does it interact with each reducer's default state?

level: middleimportance: should knowfreq 45%

basics

~20 s

preloadedState is the store's starting state, used to hydrate from the server or restore a saved session; it wins over a reducer's default, and with combineReducers any slice it omits falls back to that slice reducer's own default.

open as a page

Why does Redux keep global state in one store of plain serializable data, and what breaks if you store a Promise or class instance?

level: middleimportance: should knowfreq 50%

basics

~20 s

One store holding plain, serializable data lets you inspect, persist, hydrate from the server and replay the whole state; a Promise, Map or class instance breaks serialization and debugging tools, and objects mutated in place can stop the UI updating.

open as a page

When does a Redux store call the listeners registered with store.subscribe, and do they fire when the reducer returns the same state?

level: middleimportance: should knowfreq 40%

basics

~20 s

A Redux store calls every subscribed listener after every dispatch, even when the reducer returned the identical state object; the store never compares old and new state, so skipping unchanged data is the subscriber's job.

open as a page

In Redux Toolkit's createEntityAdapter, how do addOne, setOne, upsertOne and updateOne differ when an entity with that id already exists?

level: middleimportance: should knowfreq 36%

basics

~20 s

For an existing id, addOne does nothing, setOne replaces the whole entity, upsertOne shallow-merges the new fields into it, and updateOne shallow-merges an { id, changes } object. For a missing id, updateOne is ignored while the other three insert the entity.

open as a page

A search box built with redux-saga's takeEvery sometimes shows results for an older query. Why, and how do takeLatest and task cancellation fix it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

takeEvery forks a worker per keystroke, so requests overlap and whichever response arrives last dispatches its results, even for an older query; takeLatest cancels the previous worker before forking a new one, so only the newest query dispatches.

open as a page

In React-Redux, a 500-row table re-renders every row when one row's status changes; how do you scope useSelector so only that row re-renders?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Have the parent select only the row ids, wrap Row in React.memo and pass each an id, and let each Row select its own entity by id. A status change then changes only that row's selected value, so only it re-renders.

open as a page

In Reselect 5, how does createSelector cache results when one selector is called with different arguments from many components, and what changed from Reselect 4?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Reselect 5 memoises with weakMapMemoize by default, caching one result per distinct argument combination, so one selector serves many components. Reselect 4 cached only the last call, so alternating arguments recomputed every time and needed per-instance selectors.

open as a page

A server-rendered React app exports its Redux store as a module-level singleton, and users occasionally see another user's data. Why, and how should store creation and hydration work?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A module-level store is created once per server process and shared by every concurrent request, so one user's dispatches leak into another's render; create a fresh store per request, serialize its state into the page safely, and preload the client's own store from it.

open as a page

In Redux Toolkit, how do you stop a slow, earlier createAsyncThunk response from overwriting a newer one when a detail page fetches on every route change?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Record meta.requestId from the pending action and ignore fulfilled or rejected actions whose requestId is not the latest, and abort the superseded call from the effect cleanup with promise.abort(), passing thunkAPI.signal to fetch so the request itself is cancelled.

open as a page

Your team's Redux app routes all async logic through redux-saga. How would you decide whether to keep it or move to thunks and RTK's listener middleware?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Classify the sagas by job: fetching, imperative flows, reactive or long-running workflows. Move fetching to a data layer, imperative flows to thunks and reactive rules to the listener middleware; keep sagas only where their concurrency features earn their cost.

open as a page