Top 60 Redux Interview Questions (2026)

The 60 Redux questions interviewers actually ask, with direct answers, runnable code, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.

60 questions with answers

What Is Redux?

Key Takeaways

  • Redux is a predictable state container: state lives in one store, and the only way to change it is to dispatch a plain action object.
  • Its core is three principles: a single source of truth, read-only state, and pure reducer functions that compute the next state.
  • Modern Redux means Redux Toolkit (RTK), the official recommended way to write Redux with far less boilerplate.
  • Interviews test the data flow (action, reducer, store, subscription) and when Redux is worth its cost, not just API recall.

Redux is a predictable state container for JavaScript apps. It keeps your whole application state in a single store, and the only way to change that state is to dispatch an action, a plain object describing what happened. A pure function called a reducer takes the current state and the action and returns the next state. According to the official Redux documentation, this design rests on three principles: a single source of truth, state that's read-only, and changes made with pure functions. That one-directional flow is what makes state changes traceable and debuggable with tools like time-travel debugging. These days you rarely write Redux by hand. Redux Toolkit (RTK) is the official, recommended toolset: createSlice generates action creators and reducers together, Immer lets you write update logic that looks mutable while staying immutable, and the store comes preconfigured. In interviews, Redux questions probe the data flow and the judgment of when a global store earns its keep, so this page pairs each answer with runnable code and the reasoning behind it. If a round runs as a live AI coding interview, pair this bank with our AI interview preparation guides for the format side.

60Questions with answers on this page
3Difficulty tiers: freshers, intermediate, experienced
24Runnable code snippets you can practice from
10Quiz questions that score you as you go

Watch: React Redux Tutorials - 1 - Introduction

Video: React Redux Tutorials - 1 - Introduction (Codevolution, YouTube)

Test yourself and earn a certificate

10 quick questions. Score 70%+ to download your Redux certificate.

Jump to quiz

All Questions on This Page

60 questions
Redux Interview Questions for Freshers
  1. 1. What is Redux and what problem does it solve?
  2. 2. What are the three core principles of Redux?
  3. 3. What is an action in Redux?
  4. 4. What is a reducer and what rules must it follow?
  5. 5. What is the Redux store and what can it do?
  6. 6. Walk through the Redux data flow.
  7. 7. Why does Redux require immutable state updates?
  8. 8. What is an action creator?
  9. 9. What does dispatch do?
  10. 10. When should you use Redux instead of local component state?
  11. 11. What is the difference between Redux and React Context?
  12. 12. What is a pure function and why does it matter for reducers?
  13. 13. What is the standard shape of a Redux action?
  14. 14. Why does Redux use a single store instead of many?
  15. 15. What does combineReducers do?
  16. 16. What is the role of the Provider in react-redux?
  17. 17. What do useSelector and useDispatch do?
  18. 18. How do you set the initial state in Redux?
  19. 19. What is the Redux state tree?
  20. 20. What is the difference between getState and subscribe?
  21. 21. Why must a basic action be a plain serializable object?
Redux Intermediate Interview Questions
  1. 22. What is Redux Toolkit and why is it recommended?
  2. 23. What does createSlice do?
  3. 24. How does Redux Toolkit let you write 'mutating' code safely?
  4. 25. How is configureStore different from the classic createStore?
  5. 26. What is middleware in Redux and how does it work?
  6. 27. What is redux-thunk and why do you need it?
  7. 28. What is the difference between redux-thunk and redux-saga?
  8. 29. What does createAsyncThunk do?
  9. 30. What is the difference between reducers and extraReducers in createSlice?
  10. 31. What is a selector and why use one?
  11. 32. How does createSelector help with performance?
  12. 33. What is the connect function and how does it compare to hooks?
  13. 34. What does it mean to normalize state, and why do it?
  14. 35. What is RTK Query and when would you use it?
  15. 36. What are the Redux DevTools and what do they let you do?
  16. 37. Why can't you make an API call inside a reducer?
  17. 38. How do you avoid unnecessary re-renders with useSelector?
  18. 39. How do you reset Redux state, for example on logout?
  19. 40. What is createEntityAdapter and what does it give you?
  20. 41. How do you dispatch several actions in sequence, and should you?
Redux Interview Questions for Experienced Developers
  1. 42. How does the Redux middleware chain actually work?
  2. 43. How does Reselect memoization work, and what are its limits?
  3. 44. Beyond re-renders, why is immutability central to Redux's design?
  4. 45. How would you migrate a legacy hand-written Redux codebase to Redux Toolkit?
  5. 46. How do you keep a large Redux store performant and maintainable?
  6. 47. How do you persist and rehydrate Redux state, and what are the pitfalls?
  7. 48. How do you test reducers, thunks, and selectors?
  8. 49. Why does Redux Toolkit warn about non-serializable values in state or actions?
  9. 50. What is the RTK listener middleware and when would you use it?
  10. 51. What happens if you dispatch an action inside a reducer?
  11. 52. When would you pick Zustand or Jotai over Redux on a new project?
  12. 53. How do you implement optimistic updates in Redux?
  13. 54. Should actions model events or setters, and why does it matter?
  14. 55. Does the order of middleware matter? Give an example.
  15. 56. What is a store enhancer and how does it differ from middleware?
  16. 57. A Redux-connected list re-renders too much on scroll. How do you diagnose and fix it?
  17. 58. Is Redux still relevant given hooks, Context, and server components?
  18. 59. When should server state live in RTK Query versus a hand-written thunk slice?
  19. 60. How do you add reducers dynamically for code-split or lazy-loaded features?

Redux Interview Questions for Freshers

Freshers21 questions

The fundamentals every entry-level round checks. If any answer here surprises you, that's your study list.

Q1. What is Redux and what problem does it solve?

Redux is a predictable state container for JavaScript apps. It keeps your application state in one central store and lets you change it only by dispatching actions, which pure reducer functions handle. Because every change goes through the same explicit path, you can trace, log, and replay exactly how state got to where it is.

The problem it solves is shared state that many components read and write. Without a central store, that state gets scattered, passed through deep prop chains, and hard to trace. Redux makes every change explicit and debuggable.

Key point: A one-line definition plus the problem (scattered, hard-to-trace shared state) beats reciting the API. Interviewers open with this to hear how you frame the why, not just the what.

Watch a deeper explanation

Video: Redux in 100 Seconds (Fireship, YouTube)

Q2. What are the three core principles of Redux?

Single source of truth: the whole app state lives in one store. Read-only state: you never mutate it directly, you dispatch an action to describe a change. Changes with pure functions: reducers are pure functions that take state and an action and return the next state.

Together these give you a one-way data flow that's traceable, testable, and works with time-travel debugging.

Key point: Interviewers use this as a checkpoint. Naming all three cleanly, and explaining that read-only plus pure functions is what makes changes traceable, signals real understanding.

Q3. What is an action in Redux?

An action is a plain JavaScript object that describes something that happened in the app. It must have a type field, a string like "cart/itemAdded", and it usually carries data in a payload field. Think of it as an event record: it says what occurred, not how state should change in response.

Actions are the only way to send data to the store. You don't call a setter; you dispatch an action, and a reducer decides how state changes in response.

javascript
// a plain action object
const addItem = {
  type: "cart/itemAdded",
  payload: { id: 42, name: "Book" },
};

// an action creator returns one
function itemAdded(item) {
  return { type: "cart/itemAdded", payload: item };
}

Q4. What is a reducer and what rules must it follow?

A reducer is a pure function with the signature (state, action) => newState. It looks at the action type and returns the next state, or the current state unchanged if the action doesn't apply.

The rules: it must be pure, so no API calls, no random values, no mutating the state argument. It should return a new object or array instead of editing the existing one. Same inputs always produce the same output.

javascript
function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case "counter/incremented":
      return { ...state, value: state.value + 1 };
    case "counter/decremented":
      return { ...state, value: state.value - 1 };
    default:
      return state;
  }
}

Key point: The follow-up is almost always 'why must it be pure?'. Have the answer ready: purity is what makes state changes predictable and time-travel debugging possible.

Q5. What is the Redux store and what can it do?

The store is the object that holds your app's whole state tree, and there's exactly one per app. It ties the pieces together: it holds state, lets you read it, lets you update it by dispatching actions, and lets other code react to changes. You create it with configureStore.

Its main methods are getState() to read the current state, dispatch(action) to trigger an update, and subscribe(listener) to run a callback whenever state changes.

javascript
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({ reducer: counterReducer });

store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: "counter/incremented" }); // logs { value: 1 }

Q6. Walk through the Redux data flow.

It's a one-way loop. Something happens in the UI, so you dispatch an action. The store passes the current state and that action to the reducer. The reducer returns the next state. The store saves it and notifies every subscriber. Subscribed UI reads the new state and re-renders.

Nothing skips a step. The UI never changes state directly, which is what keeps the flow predictable.

One-way Redux data flow

1Dispatch
UI event calls store.dispatch(action)
2Reduce
store passes (state, action) to the reducer
3Update
reducer returns new state, store saves it
4Notify and re-render
subscribers run, UI reads new state

The loop is always this order. Being able to draw it from memory is what most freshers rounds check.

Key point: Draw the loop out loud. Interviewers care that you know the order and that it's one-directional, not that you memorized method names.

Watch a deeper explanation

Video: Redux Tutorial - Learn Redux from Scratch (Programming with Mosh, YouTube)

Q7. Why does Redux require immutable state updates?

Redux detects changes by comparing references. If a reducer mutates the old state object instead of returning a new one, the reference stays the same, so Redux and connected components think nothing changed and skip re-rendering.

Immutability also enables time-travel debugging (each state is a distinct snapshot) and makes reasoning about changes easier because past states never change under you.

javascript
// wrong: mutates old state, reference unchanged
state.items.push(newItem);
return state;

// right: new array, new reference
return { ...state, items: [...state.items, newItem] };

Key point: Mentioning the reference-equality check is the depth signal here. That's the actual reason mutation breaks Redux, not a style preference.

Q8. What is an action creator?

An action creator is a function that builds and returns an action object. Instead of writing the same object literal everywhere, you call a function that produces it. That keeps action shapes consistent across the codebase and removes typos in the type string, which is a common source of silent bugs.

You dispatch the result: dispatch(itemAdded(item)). Redux Toolkit's createSlice generates action creators for every reducer automatically, so you rarely hand-write them anymore.

javascript
// hand-written
function itemAdded(item) {
  return { type: "cart/itemAdded", payload: item };
}

dispatch(itemAdded({ id: 1 }));

// createSlice generates these for you
// slice.actions.itemAdded({ id: 1 })

Q9. What does dispatch do?

dispatch is the store method that sends an action into Redux, and calling store.dispatch(action) is the only way to trigger a state change. Nothing else touches the store. You dispatch an action, and Redux runs it through the middleware chain and then the root reducer to compute the next state.

After the reducer returns, the store saves that state and notifies every subscriber. In a React component you get dispatch from the useDispatch hook rather than reaching for the store directly.

javascript
import { useDispatch } from "react-redux";
import { increment } from "./counterSlice";

function Counter() {
  const dispatch = useDispatch();
  return <button onClick={() => dispatch(increment())}>+1</button>;
}

Q10. When should you use Redux instead of local component state?

Use local state (useState) for data that only one component cares about: a form input, a toggle, a hover flag. Reach for Redux when state is shared across many components far apart in the tree, needs to persist across routes, or is complex enough that tracing changes matters.

Small apps rarely need Redux at all. The honest coverage names the cost: Redux adds setup and indirection, so it earns its place only when shared state gets genuinely hard to manage.

Key point: Saying 'not everything belongs in Redux' is a green flag. Interviewers screen out people who reach for a global store by reflex.

Q11. What is the difference between Redux and React Context?

Context is a way to pass a value down the tree without prop drilling. It's not a state manager: it has no actions, reducers, or DevTools, and every consumer re-renders whenever the context value changes.

Redux is a full state management library with a strict update flow, middleware, and selective subscriptions so components re-render only for the slice they read. Context suits low-frequency values like theme or the current user; Redux suits frequently updated, widely shared state.

React ContextRedux
PurposeAvoid prop drillingManage shared state
UpdatesAll consumers re-renderOnly components reading changed slice
ToolingNone built inDevTools, time travel, middleware
Best forTheme, locale, auth userFrequent, widely shared state

Key point: The trap is calling Context a replacement for Redux. Naming the re-render behavior and the missing tooling shows you understand what each actually does.

Watch a deeper explanation

Video: Redux Tutorial (with Redux Toolkit) (Net Ninja, YouTube)

Q12. What is a pure function and why does it matter for reducers?

A pure function returns the same output for the same input and has no side effects: it doesn't mutate its arguments, call APIs, read the clock, generate random values, or touch anything outside itself. Given the same state and action, a pure reducer always returns the same next state, every single time.

Reducers must be pure so state changes stay predictable and reproducible. That's what lets Redux replay actions for time-travel debugging and makes reducers trivial to unit test.

Q13. What is the standard shape of a Redux action?

The convention is Flux Standard Action: a type field (required) and optionally payload for the data, error for a boolean error flag, and meta for extra info. Keeping to this shape makes actions consistent and tooling-friendly.

Redux Toolkit's action creators follow it automatically: the argument you pass becomes action.payload.

javascript
// Flux Standard Action shape
{
  type: "user/loginFailed",
  payload: "Invalid password",
  error: true,
}

Q14. Why does Redux use a single store instead of many?

One store means one source of truth. Every part of the app reads the same state, so there's no risk of two stores drifting out of sync and disagreeing, and debugging means inspecting one object instead of hunting across several. It also makes persistence and time-travel straightforward, because there's a single state to snapshot.

You still organize that store into slices, one per feature, and combine their reducers. So you get modular code with a single unified state tree, not one giant hand-written reducer.

Q15. What does combineReducers do?

combineReducers merges several slice reducers into one root reducer. Each slice reducer owns one key of the state object and only sees its own slice, so a cart reducer never touches user state. This is how you split a large store into small, focused reducers instead of one giant function.

With Redux Toolkit you rarely call combineReducers directly: passing a reducer object to configureStore combines them for you under the hood.

javascript
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({
  reducer: {
    cart: cartReducer,
    user: userReducer,
  },
});
// state shape: { cart: {...}, user: {...} }

Q16. What is the role of the Provider in react-redux?

Provider is a component from react-redux that makes the Redux store available to every component below it in the tree. You wrap your app once, near the root, and pass the store as a prop. Under the hood it uses React context to hand the store down, so any descendant can reach it without prop drilling.

Without the Provider, hooks like useSelector and useDispatch have no store to reach and throw an error.

jsx
import { Provider } from "react-redux";
import { store } from "./store";

<Provider store={store}>
  <App />
</Provider>

Q17. What do useSelector and useDispatch do?

useSelector reads a piece of state from the store and subscribes the component to it, so the component re-renders when that value changes. useDispatch returns the store's dispatch function so you can send actions.

They're the modern react-redux API and replace the older connect() higher-order component for most code.

jsx
import { useSelector, useDispatch } from "react-redux";
import { increment } from "./counterSlice";

function Counter() {
  const value = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  return <button onClick={() => dispatch(increment())}>{value}</button>;
}

Q18. How do you set the initial state in Redux?

Each reducer declares its own initial state as the default value of its state parameter, or as the initialState field in createSlice. When the store first starts, Redux dispatches an internal init action that no reducer handles, so every reducer falls through to its default and returns its initial state. That's how the store gets a fully populated starting tree.

You can also pass a preloadedState to configureStore, which is handy for hydrating state from the server or localStorage.

Q19. What is the Redux state tree?

The state tree is the single JavaScript object that holds all your app state, organized by slice. Each top-level key is one feature's slice of state, so you might have state.cart, state.user, and state.products side by side in one object.

Keeping it a plain, serializable object (no class instances, promises, or functions) is what lets Redux persist it, send it over the wire, and replay it in DevTools.

Q20. What is the difference between getState and subscribe?

getState() returns the current state right now: a one-time snapshot read. subscribe(listener) is different: it registers a callback that runs every time the state changes, so your code can react to updates over time. One reads once, the other keeps listening until you unsubscribe.

In a React app you rarely call either directly. react-redux wires subscribe and getState together under the hood, and you read state with useSelector instead.

javascript
const unsubscribe = store.subscribe(() => {
  console.log("state changed:", store.getState());
});

store.dispatch(increment()); // fires the listener
unsubscribe(); // stop listening

Q21. Why must a basic action be a plain serializable object?

Redux logs, replays, and time-travels through actions, and DevTools serializes them. A plain object with a string type is easy to inspect, store, and replay. Functions, promises, or class instances can't be serialized reliably, so they'd break those features.

When you need async or dynamic logic, that lives in middleware like thunks, and the middleware still dispatches plain objects to the reducer at the end.

Back to question list

Redux Intermediate Interview Questions

Intermediate20 questions

For candidates with working experience: Redux Toolkit fluency, middleware, async patterns, and the questions that separate users from understanders.

Q22. What is Redux Toolkit and why is it recommended?

Redux Toolkit (RTK) is the official, recommended way to write Redux. It packages the setup that used to be manual: configureStore sets up the store with sensible defaults, createSlice generates reducers and action creators together, and Immer lets you write update logic that looks mutable.

It exists because classic Redux was verbose: separate action types, action creators, and switch reducers for every feature. RTK removes that boilerplate and bakes in good defaults like the Redux DevTools and a serializability check.

Key point: If you still describe hand-written switch reducers as your default, it indicates out of date. Lead with RTK and mention classic Redux as the thing it replaced.

Q23. What does createSlice do?

createSlice takes a name, an initial state, and an object of reducer functions, and returns a slice reducer plus one action creator per reducer. Names are derived automatically, so a reducer called increment produces an increment action creator.

It collapses the action-type, action-creator, and reducer boilerplate into one place, and it wraps your reducers with Immer so you can write update logic that looks like mutation.

javascript
import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    incrementBy: (state, action) => { state.value += action.payload; },
  },
});

export const { increment, incrementBy } = counterSlice.actions;
export default counterSlice.reducer;

Q24. How does Redux Toolkit let you write 'mutating' code safely?

RTK runs your case reducers inside Immer. Immer hands you a draft of the current state; you mutate that draft freely, and behind the scenes Immer tracks every change you make and produces a brand-new immutable state from them. You get the readability of mutation with the correctness of immutable updates.

So state.value += 1 inside a createSlice reducer is safe: you're mutating a draft, not the real store. Outside createSlice (a plain reducer), the same code would be a bug, so know where the rule applies.

javascript
// inside createSlice: Immer makes this safe
reducers: {
  itemAdded: (state, action) => {
    state.items.push(action.payload); // looks mutable, is not
  },
}

Key point: The follow-up is 'so can you always mutate state in Redux?'. The correct answer is no: only inside Immer-wrapped reducers. Getting that boundary right is the point.

Q25. How is configureStore different from the classic createStore?

configureStore wraps createStore with good defaults. It combines your slice reducers automatically, adds redux-thunk middleware out of the box, sets up the Redux DevTools, and turns on development checks for accidental mutations and non-serializable values.

createStore is the low-level function that does none of that; you wire middleware and DevTools yourself. RTK's configureStore is the recommended entry point, and classic createStore is now marked as legacy.

javascript
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({
  reducer: { cart: cartReducer, user: userReducer },
  // thunk, DevTools, and dev-only checks are on by default
});

Q26. What is middleware in Redux and how does it work?

Middleware sits between dispatching an action and the moment it reaches the reducer. Each middleware can inspect the action, run side effects, delay or stop it, or dispatch other actions before passing it along.

It's the extension point for logging, crash reporting, and async logic. The signature is a chain of functions: store => next => action, where calling next(action) passes control to the next middleware or the reducer.

javascript
const logger = (store) => (next) => (action) => {
  console.log("dispatching", action.type);
  const result = next(action);
  console.log("next state", store.getState());
  return result;
};

Q27. What is redux-thunk and why do you need it?

Reducers are pure and dispatch expects a plain object, so neither can do async work like fetching data. redux-thunk is middleware that lets an action creator return a function instead of an object. That function receives dispatch and getState, so it can await async work and dispatch real actions when it finishes.

It's the simplest way to handle side effects in Redux, and RTK includes it by default.

javascript
function fetchUser(id) {
  return async (dispatch) => {
    dispatch({ type: "user/loading" });
    const res = await fetch(`/api/users/${id}`);
    const data = await res.json();
    dispatch({ type: "user/loaded", payload: data });
  };
}

Key point: Explaining WHY thunks exist (reducers are pure, dispatch wants a plain object, async fits neither) beats reciting the syntax.

Watch a deeper explanation

Video: Redux Crash Course With React (Traversy Media, YouTube)

Q28. What is the difference between redux-thunk and redux-saga?

Both handle async side effects, but differently. Thunks are just functions: you write async/await inline, which is simple and enough for most apps. Sagas use generator functions and run in a separate middleware, describing side effects as yielded effect objects that the library executes.

Sagas shine for complex flows: cancellation, debouncing, retries, and orchestrating many concurrent tasks. The cost is a steeper learning curve. Most teams reach for thunks or RTK Query first and add sagas only when the async logic is genuinely intricate.

redux-thunkredux-saga
Written asPlain async functionsGenerator functions
Learning curveLowHigher (generators, effects)
Best atSimple to moderate asyncComplex flows, cancellation, retries
TestabilityFine, mock dispatchStrong, effects are plain objects

Q29. What does createAsyncThunk do?

createAsyncThunk generates a thunk plus three action types for you: pending, fulfilled, and rejected. You give it a type prefix and an async function; it dispatches pending when the call starts, fulfilled with the result on success, and rejected with the error on failure.

You handle those three actions in your slice's extraReducers to update loading, data, and error state. It removes the boilerplate of writing the three actions by hand.

javascript
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";

export const fetchUser = createAsyncThunk("user/fetch", async (id) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
});

const userSlice = createSlice({
  name: "user",
  initialState: { data: null, status: "idle" },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (s) => { s.status = "loading"; })
      .addCase(fetchUser.fulfilled, (s, a) => { s.status = "done"; s.data = a.payload; })
      .addCase(fetchUser.rejected, (s) => { s.status = "error"; });
  },
});

Q30. What is the difference between reducers and extraReducers in createSlice?

The reducers field defines the actions this slice owns, and RTK auto-generates a matching action creator for each one. The extraReducers field is for responding to actions defined elsewhere: createAsyncThunk's pending, fulfilled, and rejected actions, or actions dispatched by a different slice. It doesn't create any new action creators.

So a slice uses reducers for its own actions and extraReducers to react to external ones, which keeps async lifecycle handling out of the local reducer list.

Q31. What is a selector and why use one?

A selector is a function that takes the Redux state and returns a piece of it, or something computed from it. Instead of reaching into state.cart.items directly in every component, you call selectCartItems(state). It's a small indirection that pays off: the component asks for what it needs and doesn't care how the state is shaped.

Selectors centralize the shape of state in one place, so if the state structure changes you fix one function, not fifty components. They also give you a spot to memoize derived data.

javascript
const selectCartItems = (state) => state.cart.items;
const selectCartTotal = (state) =>
  state.cart.items.reduce((sum, i) => sum + i.price, 0);

// in a component
const total = useSelector(selectCartTotal);

Q32. How does createSelector help with performance?

A selector that computes derived data (a filtered or mapped list) returns a brand-new array every call. useSelector compares by reference, so a new array every render triggers a re-render even when the underlying data didn't change.

createSelector from Reselect memoizes: it only recomputes when its input selectors return different values, and otherwise returns the same cached reference. That keeps the output stable and avoids needless re-renders.

javascript
import { createSelector } from "@reduxjs/toolkit";

const selectItems = (state) => state.cart.items;

export const selectExpensive = createSelector(
  [selectItems],
  (items) => items.filter((i) => i.price > 100) // recomputed only when items change
);

Key point: The setup for this question is often 'my component re-renders too much'. Naming reference equality as the cause and createSelector as the fix is exactly what's being tested.

Q33. What is the connect function and how does it compare to hooks?

connect is the older react-redux API: a higher-order component that injects state and dispatch into a component via mapStateToProps and mapDispatchToProps. It wraps your component and passes the selected data down as props, so the component itself stays unaware of Redux and just reads its props. It was the standard approach before hooks arrived.

The hooks useSelector and useDispatch do the same job with less indirection and are recommended for new code. connect still works and appears in older codebases, and it's occasionally useful for injecting Redux into class components.

Q34. What does it mean to normalize state, and why do it?

Normalizing means storing a collection as a lookup table keyed by id, plus a separate array of ids for ordering, instead of a deeply nested array of objects. Each entity lives in exactly one place, so the same record is never duplicated across the state tree. It's the same idea as a relational database table with a primary key.

It avoids duplication and makes updates cheap: to change one item you update one entry by id rather than mapping over a nested array. RTK's createEntityAdapter provides ready-made reducers and selectors for this shape.

javascript
// normalized shape
{
  users: {
    ids: [1, 2],
    entities: {
      1: { id: 1, name: "Asha" },
      2: { id: 2, name: "Ben" },
    },
  },
}

Q35. What is RTK Query and when would you use it?

RTK Query is Redux Toolkit's data-fetching and caching layer. You define endpoints for your API, and it generates React hooks like useGetUserQuery that handle fetching, caching, loading and error states, deduping requests, and refetching on demand. It stores the cache in Redux, so the same data isn't fetched twice across components.

Use it when most of your Redux state is really server data. It removes the need to hand-write thunks, loading flags, and cache logic for API calls. Keep plain slices for genuine client-only state.

javascript
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
  endpoints: (build) => ({
    getUser: build.query({ query: (id) => `users/${id}` }),
  }),
});

export const { useGetUserQuery } = api;

Q36. What are the Redux DevTools and what do they let you do?

The Redux DevTools are a browser extension that shows every dispatched action, the state before and after, and a diff. You can time-travel: jump back to any earlier state, replay actions, and even skip actions to see the effect.

They work because Redux state is immutable and changes only through actions, so each action is a discrete, replayable step. configureStore wires them up automatically in development.

Q37. Why can't you make an API call inside a reducer?

Reducers must be pure: same inputs, same output, no side effects. An API call is a side effect and its result is non-deterministic, so it breaks purity and would make state changes unpredictable and un-replayable.

Async work belongs in middleware: a thunk, createAsyncThunk, RTK Query, or a saga. The reducer only handles the plain actions those tools dispatch when the data arrives.

Q38. How do you avoid unnecessary re-renders with useSelector?

Select the smallest piece of state a component actually needs, not a whole object. useSelector re-runs after every dispatch and compares its result to the previous one by reference, so returning a fresh object or array each time forces a re-render even when nothing meaningful changed. Narrow selections avoid that entirely.

For derived data use memoized selectors (createSelector). When you must return a new object, pass shallowEqual as useSelector's second argument so it compares fields instead of reference.

javascript
import { useSelector, shallowEqual } from "react-redux";

// selects a new object each render: use shallowEqual
const { name, email } = useSelector(
  (s) => ({ name: s.user.name, email: s.user.email }),
  shallowEqual
);

Q39. How do you reset Redux state, for example on logout?

Dispatch a single top-level action, say auth/loggedOut, and handle it in a wrapper around the root reducer. When that action arrives, you ignore the current state and pass undefined down instead, which makes every slice fall back to its own initial state. It's a clean, one-place reset rather than clearing each slice by hand.

The pattern is to wrap the combined reducer: if the action is the reset one, call the underlying reducer with undefined so every slice re-initializes.

javascript
const appReducer = combineReducers({ user, cart });

function rootReducer(state, action) {
  if (action.type === "auth/loggedOut") {
    state = undefined; // every slice resets to its initial state
  }
  return appReducer(state, action);
}

Q40. What is createEntityAdapter and what does it give you?

createEntityAdapter is an RTK helper for managing normalized collections. It stores items as { ids: [], entities: {} } and hands you prewritten CRUD reducers (addOne, upsertMany, removeOne, updateOne) plus memoized selectors (selectAll, selectById).

It saves you from hand-writing the same normalized update logic for every list, and it pairs cleanly with createAsyncThunk to load and store fetched data.

javascript
import { createEntityAdapter, createSlice } from "@reduxjs/toolkit";

const usersAdapter = createEntityAdapter();

const usersSlice = createSlice({
  name: "users",
  initialState: usersAdapter.getInitialState(),
  reducers: {
    userAdded: usersAdapter.addOne,
    usersReceived: usersAdapter.setAll,
  },
});

Q41. How do you dispatch several actions in sequence, and should you?

You can call dispatch multiple times inside a thunk, and each runs the full reducer cycle. That's fine for a real sequence, like dispatching a loading action, then a success action after a fetch.

But don't split one logical change across many actions just to update several slices; that causes several re-render passes and a noisy log. Prefer one well-named event action that multiple reducers respond to, which keeps the change atomic.

javascript
const checkout = () => async (dispatch) => {
  dispatch(checkoutStarted());
  const order = await api.placeOrder();
  dispatch(checkoutSucceeded(order)); // cart and orders slices both react
};
Back to question list

Redux Interview Questions for Experienced Developers

Experienced19 questions

advanced rounds probe internals, architecture judgment, and production scars. Expect every answer here to draw a follow-up.

Q42. How does the Redux middleware chain actually work?

applyMiddleware composes middleware into a pipeline around dispatch. Each middleware has the shape store => next => action. The store's real dispatch is the last next in the chain; each middleware wraps the next one.

When you dispatch, the action flows through each middleware in order. A middleware can act before calling next(action), after it, both, or never (stopping the action). Because each holds a reference to store.dispatch, it can also dispatch new actions, which restart at the top of the chain.

javascript
// middleware is composed so next points at the following one
const chain = middlewares.map((mw) => mw(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
// dispatch(action) now runs the whole pipeline

Key point: Being able to describe next as 'the next middleware, or the real dispatch if you're last' is the detail that separates users from people who understand the chain.

Q43. How does Reselect memoization work, and what are its limits?

createSelector takes input selectors and a result function. It caches the last inputs and last output. On each call it runs the input selectors; if every result is reference-equal to last time, it returns the cached output without recomputing.

The classic limit is a cache size of one: a selector shared by multiple components with different arguments thrashes the cache, recomputing every call. The fix is a selector factory (a function that returns a fresh createSelector per component) or a weak-map-based memoizer.

javascript
// factory: each component gets its own memoized selector
const makeSelectItemsByCategory = () =>
  createSelector(
    [(state) => state.items, (state, category) => category],
    (items, category) => items.filter((i) => i.category === category)
  );

Q44. Beyond re-renders, why is immutability central to Redux's design?

Immutability makes state changes into a sequence of discrete snapshots. That enables three things at once: cheap change detection by reference comparison, time-travel debugging and action replay, and safe concurrent reads because no one mutates state under you.

It also makes reducers trivially testable: given a state and an action, assert on the returned state, with no setup or teardown. The whole predictability story rests on old state never changing.

Q45. How would you migrate a legacy hand-written Redux codebase to Redux Toolkit?

Incrementally, slice by slice. RTK is designed to interoperate with classic Redux, so you don't rewrite everything at once. Start by swapping createStore for configureStore, which keeps existing reducers working while adding DevTools and the mutation and serializability checks.

Then convert one feature at a time: replace its action types, action creators, and switch reducer with a single createSlice. Convert manual thunks to createAsyncThunk. Move pure API-cache slices to RTK Query last, since that changes the component data-access pattern.

Incremental RTK migration

1Swap the store
createStore to configureStore, old reducers still run
2Convert slices
replace types, creators, switch reducer with createSlice
3Modernize async
hand thunks to createAsyncThunk
4Adopt RTK Query
move server-cache slices last

Ship each step separately. Because RTK interoperates with classic Redux, the app stays working the whole way.

Key point: The signal here is 'incremental, not big-bang'. Naming configureStore as the safe first step and RTK Query as the last shows you've actually done a migration.

Q46. How do you keep a large Redux store performant and maintainable?

Normalize entities into id-keyed lookup tables (createEntityAdapter) so updates touch one record and no data is duplicated. Keep derived data out of the store and compute it in memoized selectors instead. Split state into feature slices that own their shape, and colocate their reducers, selectors, and thunks.

For rendering, select narrow slices, memoize derived selectors, and lean on RTK Query for server cache so you're not hand-managing loading flags. Avoid deeply nested state, which makes immutable updates verbose and error-prone.

Q47. How do you persist and rehydrate Redux state, and what are the pitfalls?

redux-persist saves the store, or a chosen subset of slices, to storage like localStorage and rehydrates it when the app loads. You wrap your reducer with persistReducer, whitelist only the slices worth saving, and gate the initial render behind a PersistGate so the UI waits until rehydration finishes before it reads state.

Pitfalls: don't persist server cache (it goes stale, prefer RTK Query), version and migrate the persisted shape or old data crashes new reducers, keep persisted state serializable, and never store secrets in localStorage. Persisting everything blindly is the common mistake.

Q48. How do you test reducers, thunks, and selectors?

Reducers are pure, so testing is direct: call the reducer with a state and an action, assert on the returned state. No mocks needed. Selectors are the same: pass a state object, assert the output.

Thunks need dispatch and any API mocked. With createAsyncThunk you can dispatch it against a real store configured for the test and assert the resulting state, or mock fetch and check the dispatched pending and fulfilled actions. Testing behavior through the store beats asserting exact internal action sequences.

javascript
test("increment adds one", () => {
  const next = counterReducer({ value: 0 }, increment());
  expect(next.value).toBe(1);
});

Q49. Why does Redux Toolkit warn about non-serializable values in state or actions?

RTK's serializability middleware flags values like Promises, class instances, Dates, Maps, or functions in the state or in actions. Redux assumes serializable data so it can persist state, send it to DevTools, and time-travel reliably; non-serializable values break those guarantees.

The right fixes are to store serializable data (an ISO string instead of a Date, an id instead of a class instance) and keep functions and promises out of state. For unavoidable cases you can configure the middleware to ignore specific paths or action types.

Q50. What is the RTK listener middleware and when would you use it?

createListenerMiddleware lets you run logic in response to dispatched actions or state changes, without generators. You register listeners that match actions (or a predicate) and run effects: debouncing, conditional dispatch, cancellation via a fork/take-style API.

It's the recommended lighter alternative to redux-saga for reactive side effects. Use it when thunks aren't enough because you need to react to actions across features, but you don't want the full saga machinery.

javascript
import { createListenerMiddleware } from "@reduxjs/toolkit";

const listener = createListenerMiddleware();
listener.startListening({
  actionCreator: itemAdded,
  effect: async (action, api) => {
    api.dispatch(recalculateTotals());
  },
});

Q51. What happens if you dispatch an action inside a reducer?

You can't safely, and Redux guards against it: dispatching while the reducer is running throws, because a reducer must be a pure function of state and action, not a place that triggers more state changes. Allowing it would make the update order impossible to reason about.

If one change should trigger another, do it outside the reducer: dispatch a second action from a thunk or the listener middleware after the first completes, or handle both concerns in a single well-designed action.

Q52. When would you pick Zustand or Jotai over Redux on a new project?

For small or mid-size apps where the ceremony of Redux outweighs its benefits, Zustand (a single hook-based store, minimal boilerplate) or Jotai (atomic, bottom-up state) often fit better. They need less setup and have a gentler learning curve.

Redux still wins when you want strict conventions across a large team, deep DevTools and time travel, a mature middleware ecosystem, or RTK Query's caching. The honest answer weighs team size, app complexity, and how much you value traceability against setup cost.

Redux (RTK)ZustandJotai
BoilerplateMore, with conventionsMinimalMinimal
ModelSingle store, actionsSingle store, direct setAtoms, bottom-up
DevTools / time travelStrong, built inBasicBasic
Best forLarge apps, big teamsSmall to mid appsFine-grained atomic state

Q53. How do you implement optimistic updates in Redux?

Update the store immediately as if the request succeeded, keep the previous value, then reconcile when the server responds. On success you keep the optimistic state; on failure you roll back to the saved value and surface an error.

In RTK Query you do this with onQueryStarted: apply an updateQueryData patch right away, and call patchResult.undo() if the request rejects. With plain thunks you snapshot the old state, dispatch the optimistic change, and dispatch a revert action on error.

javascript
// RTK Query optimistic update
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
  const patch = dispatch(
    api.util.updateQueryData("getTodos", undefined, (draft) => {
      draft.push(arg); // show it immediately
    })
  );
  try {
    await queryFulfilled;
  } catch {
    patch.undo(); // roll back on failure
  }
}

Q54. Should actions model events or setters, and why does it matter?

Model actions as events that describe what happened (cart/itemAdded, checkout/failed), not as setters that describe what to change (setCartItems). Event-style actions keep intent in one place, let many reducers respond to a single event, and produce a readable, auditable log in DevTools.

Setter-style actions push logic into components (the component computes the next state and just stores it), which scatters business rules and defeats the point of a central reducer. This distinction is a senior-level design signal.

Key point: the key signal is 'actions are events, not setters'. It's a small phrase that signals you've felt the pain of a setter-heavy store.

Q55. Does the order of middleware matter? Give an example.

Yes. Middleware runs in the order you register it, wrapping outward, so position changes behavior. A logger placed before the thunk middleware logs the raw function-typed action; placed after, it logs the plain actions the thunk actually dispatches.

The practical rule: put middleware that should see final, plain actions (loggers, analytics) after the ones that transform or intercept actions (thunk, saga, listener). RTK's getDefaultMiddleware lets you insert yours at the right spot with prepend or concat.

Q56. What is a store enhancer and how does it differ from middleware?

Middleware customizes dispatch: it wraps the dispatch function to intercept actions. A store enhancer is higher-level: it wraps the whole createStore, so it can override any part of the store, including getState, subscribe, and dispatch.

applyMiddleware is itself implemented as an enhancer. You'd write a custom enhancer only for cross-cutting store behavior that middleware can't reach, like the Redux DevTools enhancer or persistence wiring. Most apps never need a hand-written one.

Q57. A Redux-connected list re-renders too much on scroll. How do you diagnose and fix it?

Diagnose first: use the React DevTools profiler and the Redux DevTools to see which components render and which actions fire. The usual causes are selectors returning new references each call, subscribing to too much state, or every row reading the whole list.

Fixes in order: memoize derived selectors with createSelector, have each row select only its own item by id (so unrelated updates don't touch it), wrap rows in React.memo, and virtualize the list so only visible rows mount. Measure after each change rather than applying all at once.

Key point: in disguise. 'profile, then fix the biggest cause, then re-measure' over a list of tricks recited without diagnosis is the technical point.

Q58. Is Redux still relevant given hooks, Context, and server components?

Yes, for the problems it was built for. Hooks and Context handle local and low-frequency shared state well, and server components move some data-loading off the client, so Redux is no longer the default for every app. That's a good thing: it's now used where it earns its place.

Where it stays strong: large client-heavy apps with complex, frequently updated shared state, teams that value strict conventions and DevTools, and codebases using RTK Query for cache management. The honest take is that Redux got narrower and better, not obsolete.

Key point: The wrong answer is either 'Redux is dead' or 'always use Redux'. The mature take is that its scope narrowed and RTK made it worth using where it fits.

Watch a deeper explanation

Video: Redux Tutorial - Beginner to Advanced (freeCodeCamp.org, YouTube)

Q59. When should server state live in RTK Query versus a hand-written thunk slice?

RTK Query fits data that's owned by the server: lists, records, and anything you fetch, cache, and refetch. It handles caching keys, deduping, loading and error flags, and cache invalidation with tags, which is logic you'd otherwise write by hand in thunk slices.

Hand-written thunks earn their place when the async flow is unusual: multi-step orchestration, heavy local transformation before storing, or state that isn't really a cached server resource. Mixing both is normal: RTK Query for the API cache, plain slices for genuine client state.

RTK QueryThunk slice
Best forCached server dataCustom async flows, client logic
CachingBuilt in, tag-based invalidationYou write it
Loading and error stateAuto in the generated hookYou track it manually
ControlConvention-drivenFull, but more code

Q60. How do you add reducers dynamically for code-split or lazy-loaded features?

Keep a registry of the currently loaded slice reducers, and when a lazy feature mounts, add its reducer and rebuild the root reducer with store.replaceReducer. This keeps the initial bundle small: a feature's reducer ships only when the feature loads.

The pattern is a small injectReducer helper that stores the new reducer, recombines everything, and calls replaceReducer. RTK Query's api reducer and middleware can be added the same way. Guard against injecting the same key twice.

javascript
const staticReducers = { user: userReducer };
const asyncReducers = {};

function injectReducer(store, key, reducer) {
  if (asyncReducers[key]) return;
  asyncReducers[key] = reducer;
  store.replaceReducer(
    combineReducers({ ...staticReducers, ...asyncReducers })
  );
}

Key point: This comes up for large single-page apps. Naming replaceReducer and the duplicate-injection guard shows you've actually shipped code-split Redux, not just read about it.

Back to question list

Redux vs Context, MobX, Zustand, and Recoil

Redux wins when you need predictable, traceable updates to shared state across a large app, with strong tooling and a strict one-way data flow. It costs more setup and ceremony than lighter tools, so it's overkill for a handful of shared values. React Context passes data down without prop drilling but isn't a state manager: every consumer re-renders when the value changes. Zustand and Recoil are lighter modern options with less boilerplate. MobX trades Redux's explicit dispatch for observable, reactive updates. Saying these trade-offs out loud is itself an interview signal: it shows you pick tools on merits, not habit.

ToolPatternBest atWatch out for
Redux (RTK)Single store, dispatch actionsLarge shared state, strict traceability, DevToolsMore setup than lighter tools
React ContextProvider passes a value downLow-frequency values like theme or authEvery consumer re-renders on change
ZustandHook-based store, direct setSmall to mid apps wanting minimal boilerplateFewer built-in conventions
MobXObservable reactive stateReactive updates, less explicit wiringImplicit reactivity is harder to trace
RecoilAtoms and selectorsFine-grained, per-atom subscriptionsSmaller ecosystem, less mature

How to Prepare for a Redux Interview

Prepare in layers, and practice out loud. Most Redux rounds move from concept questions (what's an action, what's a reducer) to writing a slice or a selector, to a discussion of trade-offs and async patterns. Rehearse each stage rather than only reading answers.

  • Learn Redux Toolkit first: createSlice, configureStore, and createAsyncThunk are what the question expects in 2026, not hand-written switch reducers.
  • Trace the data flow until you can draw it from memory: dispatch an action, reducer returns new state, store notifies subscribers, UI re-renders.
  • Type and run every snippet; building a tiny counter and then a fetch-with-thunk slice cements the ideas far faster than reading.
  • Take the quiz on this page to find weak spots, then revisit those questions before the real round.

The typical Redux interview flow

1Concept screen
actions, reducers, store, why immutability, when to use Redux
2Write a slice
createSlice with reducers, or a selector, live
3Async and middleware
thunks, createAsyncThunk, RTK Query, side effects
4Trade-offs and debugging
Redux vs Context, re-render issues, DevTools

Earlier rounds increasingly run as AI coding interviews. The data flow on this page is what gets probed at every stage.

Test Yourself: Redux Quiz

Ready to test your Redux knowledge?

10 questions, about 6 minutes. Score 70% or higher to earn a shareable certificate.

10 questions Instant feedback Free certificate on 70%+

Frequently  Asked  Questions

Which Redux topics should I prioritize before a technical round?

Prioritize the areas that change real decisions: fundamentals, practical tasks, debugging, trade-offs, and evidence. The highest-value questions are tied to a project example, a validation check, or a failure mode.

Should I learn classic Redux or Redux Toolkit?

Redux Toolkit, and it isn't close. RTK is the official recommended way to write Redux, and most teams have moved to createSlice and configureStore. Understand the classic action-reducer-store concepts because interviewers ask about them, but write your code with RTK. Knowing both, and being able to say why RTK replaced the boilerplate, indicates current experience.

Do I need to know React to answer Redux questions?

Redux itself is framework-agnostic, so the core (store, actions, reducers) stands on its own. In practice almost every Redux job pairs it with React, so expect questions about react-redux: the Provider, useSelector, and useDispatch. If the role is React-based, prepare the binding layer alongside the core.

How long does it take to prepare for a Redux interview?

If you've shipped a React app with Redux, one week of an hour a day covers this bank with practice runs. Starting colder, plan two to three weeks and build small apps daily: a counter, then a todo list, then a slice that fetches from an API. Reading answers without typing them is how preparation quietly fails.

Do interviewers ask exactly these questions?

The concepts repeat far more reliably than the exact wording. Interviewers rephrase, chain follow-ups, and push toward whatever you claim confidence in. Prepare the underlying ideas, the one-way data flow, immutability, pure reducers, middleware, and the phrasing takes care of itself.

Is there a way to test my Redux knowledge?

Yes. The quiz on this page scores you as you go and explains every answer, so you find your weak spots fast. Pass the threshold and you can download a shareable certificate with your name and score, free and with no sign-up. It's the quickest way to check yourself before the real round.

From the team that builds coding interviews

Hyring builds the AI Coding Interviewer that runs live technical rounds in 20+ languages for 5,000+ hiring teams. These Redux questions and scoring notes reflect what actually gets asked and evaluated inside the interviews we host.

See how the AI Coding Interviewer works

Sources

Adithyan RKWritten by Adithyan RK
Surya N
Fact-checked by Surya N
Published on: 24 May 2026Last updated: 19 Jul 2026
Share: