Redux interviews have moved past hand-writing action constants and large switch statements. The questions now ask why createSlice lets you write code that looks like mutation, what createAsyncThunk dispatches and when Redux is a better fit than Context.
The whole answer becomes easier when you trace one feature from component to store and back.
The Redux mental model in one loop
Redux keeps shared application state in a store. A component dispatches an action, which is a plain object with a type and often a payload. A reducer uses the current state and that action to calculate the next state. Components select the data they need and render again when that selected data changes.
The loop is:
component -> dispatch(action) -> reducer -> next store state -> selector -> componentClassic Redux reducers must be pure. Given the same state and action, they should calculate the same next state without changing the existing state object or producing unrelated side effects. Network calls, timers and random values do not belong inside a reducer.
This predictable action history is the reason Redux can support strong debugging tools. Every state change has an explicit cause.
Why Redux Toolkit is the default
Classic Redux often required four separate pieces for one operation: an action-type constant, an action creator, a switch case and a manual immutable update. Nested state made the spread-operator code especially noisy.
Redux Toolkit provides the standard tools in one package:
configureStorecreates the store with useful defaults.createSlicedefines a feature's initial state, reducer logic and generated actions.Immer lets slice reducers use convenient mutation-like syntax while producing immutable next state.
createAsyncThunkcreates a standard lifecycle around promise-based work.
The data flow remains Redux. Toolkit removes repeated setup and makes the safe path the normal path.
createSlice, worked from value 0 to 5
Define a counter feature:
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0,
status: "idle"
},
reducers: {
increment: state => {
state.value += 1;
},
addBy: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, addBy } = counterSlice.actions;
export default counterSlice.reducer;createSlice generates an increment action creator with type "counter/increment" and an addBy action creator with type "counter/addBy".
Calling addBy(5) creates an action equivalent to:
{ type: "counter/addBy", payload: 5 }When the reducer handles it, state.value += action.payload appears to modify state. In reality, Immer tracks writes to a draft and produces the immutable next state. The original value is 0, the payload is 5, and 0 + 5 = 5, so the next state's value is 5.
Do not return a separate value and mutate the draft in the same case. Use the draft syntax or return a replacement state consistently.

configureStore and read the state
Register the slice reducer under a state key:
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
export const store = configureStore({
reducer: {
counter: counterReducer
}
});The key creates the path state.counter. In a React component:
const value = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<button onClick={() => dispatch(addBy(5))}>
{value}
</button>
);useSelector reads the value and subscribes the component to relevant store updates. useDispatch returns the store's dispatch function. In a larger application, export named selector functions so components do not all repeat knowledge of the state shape.
createAsyncThunk for side effects
Reducers cannot perform a network request, so put asynchronous work outside them. A thunk can dispatch around that work:
import { createAsyncThunk } from "@reduxjs/toolkit";
export const fetchCount = createAsyncThunk(
"counter/fetchCount",
async amount => {
const res = await api(amount);
return res.data;
}
);Dispatching fetchCount(10) automatically creates a promise lifecycle with three action types:
counter/fetchCount/pendingcounter/fetchCount/fulfilledcounter/fetchCount/rejected
Handle them in extraReducers because these actions are generated outside the slice's ordinary reducers field:
extraReducers: builder => {
builder
.addCase(fetchCount.pending, state => {
state.status = "loading";
})
.addCase(fetchCount.fulfilled, (state, action) => {
state.status = "idle";
state.value += action.payload;
})
.addCase(fetchCount.rejected, state => {
state.status = "failed";
});
}The value returned by the payload creator becomes action.payload in the fulfilled case. If the counter is already 5 and the API returns 10, the reducer calculates 5 + 10 = 15. A second check from the action sequence gives the same result: addBy(5) moves 0 to 5, then the fulfilled payload adds 10 and moves 5 to 15.
The pending case changes only status. The fulfilled case stores the result and clears loading. The rejected case must leave the UI with a state it can explain.
Redux vs Context: when each one fits
Context is useful for making values such as a theme, locale or current-user object available through a component tree. Context itself does not provide Redux's action log, middleware model or time-travel debugging.
Redux is a stronger fit when shared state changes frequently, multiple parts of the application coordinate updates, asynchronous workflows need explicit status, or the team benefits from a predictable action history. Context and Redux are not mutually exclusive. An application can use each for the problem it handles well.
A practical split is to keep Context for values that are set once and read widely, and to move anything with a lifecycle, a status field or a history worth replaying into the store. React interview questions for freshers takes the wider set: hooks and the stale-closure trap, reconciliation and keys, and the point where local state stops being enough.
The KnowledgeGate question bank carries Redux practice items on the same mechanics: which method creates the store, which hooks dispatch and select a slice, what the spread operator is doing in a classic reducer, and which rules a reducer has to obey.
Redux Toolkit interview questions, answered
Why can a createSlice reducer look like it mutates state? Toolkit runs slice reducers inside Immer. The line state.value += action.payload writes to a draft proxy, and Immer replays those writes onto a fresh object. The store still receives a new state, which is why 0 + 5 = 5 arrives as a new value rather than an edited one.
What action types does createSlice generate? One per key in the reducers field, named slice/reducer. The counter slice above generates counter/increment and counter/addBy, and addBy(5) builds the object { type: "counter/addBy", payload: 5 }.
Name the three actions createAsyncThunk dispatches. counter/fetchCount/pending, counter/fetchCount/fulfilled and counter/fetchCount/rejected. Only the fulfilled action carries the payload creator's return value, which is how the counter moves from 5 to 15 when the API returns 10.
Why do thunk actions go in extraReducers rather than reducers? The reducers field both handles and generates its actions, so it can only hold cases the slice itself owns. The three lifecycle actions are created by createAsyncThunk outside the slice, so the slice listens for them as external actions in extraReducers.
How does a component read counter state out of the store? Through the key you register in configureStore. Passing reducer: { counter: counterReducer } creates the path state.counter, so the component calls useSelector(state => state.counter.value) and re-renders when that one value changes. useDispatch supplies the dispatch function the button needs for addBy(5).
What breaks if a reducer calls an API directly? Purity. A reducer is expected to return the same next state for the same state and action, and a network call breaks that: the response lands long after the reducer has returned, replay and tests stop being reliable, and the action log no longer explains the state it produced. The request belongs in a thunk.
The short version and next step
One store holds shared state. Components dispatch actions, reducers calculate the next state, and selectors read it. Redux Toolkit provides createSlice, Immer-backed reducer syntax, configureStore and createAsyncThunk with pending, fulfilled and rejected actions.
Use the Complete React and Redux course for the full state-management path and the MERN Stack and DSA bundle for integration practice. The algorithm round usually falls on the same interview day as the frontend one, and Coding and DSA courses for placements is where that half of the preparation lives.




