skip to content

State Management

The libraries that hold state outside components — single stores, atoms, observables — and the trade-offs between them. Interviewers ask because deciding where state lives and how it is derived is what keeps a growing frontend codebase changeable.

on this pageshow

explore

→ has its own guide

questions

203 · 10 sections

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%
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.

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 RTK Query, which hooks does createApi generate for a getPosts query and an updatePost mutation, and how do isLoading and isFetching differ?

level: juniorimportance: must knowfreq 62%
basics
~20 s

RTK Query's React createApi generates useGetPostsQuery and useLazyGetPostsQuery for the query and useUpdatePostMutation for the mutation. isLoading is true only while the hook has no data yet; isFetching is true whenever a request is in flight, including refetches.

open as a page

In RTK Query, how do providesTags and invalidatesTags refresh a posts list and a post detail after updatePost, and why add a 'LIST' id?

level: middleimportance: must knowfreq 58%
basics
~20 s

In RTK Query, queries declare providesTags and mutations declare invalidatesTags; after a mutation, subscribed queries with a matching tag refetch. Tag each post { type: 'Post', id } and the list also with id 'LIST', so edits and additions refetch only what they affect.

open as a page

In RTK Query, when does a cached query result get reused, refetched or removed, given keepUnusedDataFor and refetchOnMountOrArgChange?

level: middleimportance: should knowfreq 45%
basics
~20 s

RTK Query reuses a cached result while any subscriber holds it and does not refetch on mount by default. keepUnusedDataFor (60 seconds) removes it after the last subscriber leaves; refetchOnMountOrArgChange forces or age-limits refetching on subscribe.

open as a page

In RTK Query, what must you add to configureStore for an api slice, and what breaks if the middleware or setupListeners is missing?

level: middleimportance: should knowfreq 48%
basics
~20 s

An RTK Query api slice needs its reducer mounted at api.reducerPath and api.middleware appended to the store. Without the middleware, cache lifetimes, invalidation and polling do not run; without setupListeners, refetchOnFocus and refetchOnReconnect never fire.

open as a page

In RTK Query, how do you make an updatePost mutation update the cached post optimistically and roll back if the request fails?

level: seniorimportance: should knowfreq 40%
basics
~20 s

In RTK Query, give the mutation an onQueryStarted callback that dispatches api.util.updateQueryData to patch the cached post, keeps the returned patchResult, awaits queryFulfilled, and calls patchResult.undo() if it rejects. Invalidating tags on failure is the safer rollback when edits overlap.

open as a page

With Zustand 5, how do you create a store, read it from a component, and update it without wrapping the app in a Provider?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Zustand's create((set, get) => ({...})) returns a hook bound to a module-level store. Components call that hook with a selector to read a slice, and actions defined inside the store call set to update it, so no Provider is needed.

open as a page

In Zustand 5, how do you read several fields from a store in one hook call without extra re-renders, and what replaced v4's equality-function argument?

level: middleimportance: must knowfreq 55%
basics
~20 s

Wrap the selector in useShallow from zustand/shallow: it returns the previous object or array when the new one is shallow-equal, so the hook sees a stable reference. v5 dropped the hook's equality argument; createWithEqualityFn in zustand/traditional keeps the old style.

open as a page

In Zustand, what does set() do with the object you pass it, and when would you pass true as its second argument?

level: middleimportance: should knowfreq 48%
basics
~20 s

set shallow-merges the object into a copy of the current state: omitted top-level keys survive, but nested objects you pass replace the old ones wholesale. Passing true as the second argument replaces the state outright, used for resets or removing keys.

open as a page

With Zustand 5, how do you give each server request, test or widget its own store instance instead of the single module-level store that create builds?

level: seniorimportance: should knowfreq 40%
basics
~20 s

Build the store with a factory around createStore, create one instance per provider mount with useState(() => createCartStore(initial)), pass that instance through React Context, and read it with useStore(store, selector). Each request, test or widget then owns an isolated store.

open as a page

A Zustand cart store wrapped in persist gains a nested shipping.method field, and returning users see it as undefined after the release; why, and how do you fix it?

level: seniorimportance: should knowfreq 36%
basics
~20 s

persist rehydrates with a shallow merge, so the stored shipping object replaces the new default one and method never appears. Bump the persist version and add a migrate function that fills the field, or supply a deep merge.

open as a page

In Jotai, what are primitive, read-only derived and writable derived atoms, and which hook do you use to read or write each?

level: juniorimportance: must knowfreq 45%
basics
~20 s

atom(initial) makes a primitive atom; atom(get => ...) a read-only derived atom; atom(read, write) a writable derived one, with atom(null, write) as a write-only action. useAtom returns [value, set], useAtomValue the value, useSetAtom only the setter.

open as a page

In Jotai, what changes when you wrap part of the tree in a Provider, and how do createStore and getDefaultStore relate to it?

level: middleimportance: should knowfreq 30%
basics
~20 s

A Provider gives its subtree its own store, so the same atoms hold separate values there; without one, hooks use the default store from getDefaultStore. createStore makes a store to pass to Provider and to use outside React.

open as a page

When one form field changes, how does Jotai decide what recomputes and re-renders, compared with a single Zustand store holding all the fields?

level: middleimportance: should knowfreq 40%
basics
~20 s

Jotai follows the dependency graph recorded by get: writing one field atom recomputes only the mounted atoms that read it and notifies only atoms whose value changed. A single Zustand store notifies every subscriber, and each selector filters.

open as a page

In Jotai 3, what happens when a component reads an async atom with useAtomValue, and how do you avoid suspending or cancel a stale request?

level: seniorimportance: should knowfreq 35%
basics
~20 s

An async atom's value is a promise, so useAtomValue suspends the reader until it settles; a Suspense boundary must sit above it and errors reach an error boundary. unwrap gives a non-suspending view, and the read's signal aborts stale fetches.

open as a page

A Jotai form component creates its field atoms inside the render body and then loops or loses every typed value; why, and how should per-field atoms be created instead?

level: seniorimportance: should knowfreq 33%
basics
~20 s

Jotai identifies atoms by object identity, so an atom created during render is a brand-new key each time: its value starts over and useAtom can loop. Define atoms at module scope, memoise them with useMemo, or use atomFamily from jotai-family in v3.

open as a page

In MobX 6, what is the difference between makeObservable with annotations and makeAutoObservable, and when can you not use makeAutoObservable?

level: juniorimportance: must knowfreq 42%
basics
~20 s

makeObservable(this, { ... }) annotates only the members you list; makeAutoObservable(this) infers them: fields become observable, getters computed, functions autoAction, generators flow. makeAutoObservable cannot be used on classes that have a superclass or are subclassed.

open as a page

In MobX 6, why does assigning observable state after an await inside a store method trigger a strict-mode warning, and how do you fix it?

level: middleimportance: must knowfreq 42%
basics
~20 s

An action only covers the synchronous part of a function; code after await runs later, outside it. With the default enforceActions: "observed", changing observed state there logs a warning. Wrap the post-await writes in runInAction, or write the method as a flow generator.

open as a page

A React todo row shows a stale title after a MobX store updates todo.title; what do you check, and why must the component that renders the value be an observer?

level: seniorimportance: must knowfreq 45%
basics
~20 s

MobX tracks only reads made during an observer's own render. Check that the row is an observer, reads todo.title itself rather than receiving a pre-read string, is not rendered through a non-observer child or callback, and that title is observable.

open as a page

In MobX, when should derived data be a computed value rather than an autorun or reaction, and when does a computed actually cache?

level: middleimportance: should knowfreq 36%
basics
~20 s

Use computed for any value derived from state and reactions only for side effects such as saving or logging. A computed is lazy and caches its result only while something observes it; read outside a reaction, it recomputes on every access.

open as a page

A MobX autorun in a todo app re-runs more often than expected and keeps running after its screen closes; how do you find what it tracks, and how do you stop it?

level: seniorimportance: should knowfreq 30%
basics
~20 s

An autorun depends on everything it read in its last run, so serialising the whole list makes any field change re-run it. trace() or getDependencyTree shows those dependencies, reaction narrows them, and calling the returned disposer on unmount stops it.

open as a page

In NgRx, why does a flight-search page's search request belong in an effect rather than in the reducer or the component?

level: juniorimportance: must knowfreq 64%
basics
~20 s

An NgRx reducer must be a pure, synchronous function from state and action to the next state, so it cannot wait for an API. An effect listens to dispatched actions, runs the request and dispatches a success or failure action back.

open as a page

In NgRx's @ngrx/entity, why does a kanban board keep its cards as an ids array plus an entities map instead of one array?

level: juniorimportance: must knowfreq 44%
basics
~20 s

EntityState splits a collection into ids, which hold the order, and entities, a map keyed by id, so a card is found by key instead of a scan, is stored once, and can change without rebuilding the order.

open as a page

In NgRx 22, why must an on() handler inside createReducer return a new state object instead of mutating the slice it receives?

level: juniorimportance: must knowfreq 62%
basics
~20 s

An NgRx reducer is a pure function whose new object is how the store detects a change: selectors and select() compare by reference. In development the default immutability check freezes state, so a mutation throws instead of failing silently.

open as a page

In NgRx 22, why should an invoicing dashboard's components read the invoice slice through selectors instead of mapping store state themselves?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Selectors keep knowledge of the state shape in one file, compute derived values such as invoice totals once and cache them, compose into larger reads, and are pure functions that can be tested without a component.

open as a page

What is an NgRx SignalStore, and how does it differ from an Angular service that keeps its own writable signals?

level: juniorimportance: must knowfreq 62%
basics
~20 s

An NgRx SignalStore is an injectable class built by signalStore() from features: withState turns state slices into signals, withComputed and withMethods add derived values and operations, and by default only the store's own methods can change state.

open as a page

In NGXS, how do you define a state slice with @State, change it from an @Action handler, and register it in a standalone Angular app?

level: juniorimportance: must knowfreq 38%
basics
~10 s

An NGXS state is a class decorated with @State({ name, defaults }) and @Injectable(); its @Action(ActionClass) methods receive a StateContext and the action and write through setState or patchState. provideStore([RoomsState]) registers it.

open as a page

In NGXS 22, how do @Selector methods and createSelector memoize, and how should a standalone component read them now that the @Select decorator is deprecated?

level: middleimportance: must knowfreq 34%
basics
~20 s

@Selector marks a static method whose result is cached on its last inputs; createSelector builds the same kind of selector at runtime. Components read them with select(), which returns a signal, or Store.select for an Observable; @Select is deprecated.

open as a page

In NGXS, how do ctx.setState and ctx.patchState differ, and when should an action handler use state operators such as patch and updateItem instead?

level: middleimportance: should knowfreq 30%
basics
~10 s

setState replaces the whole slice and also accepts a state operator; patchState shallow-merges top-level properties into an object slice. State operators like patch and updateItem express nested immutable updates and keep unchanged references intact.

open as a page

In an NGXS hotel booking app, a room-search action fires on every keystroke and older results overwrite newer ones; why, and how do cancelUncompleted and ctx.abortSignal fix it?

level: seniorimportance: should knowfreq 24%
basics
~20 s

NGXS runs async action handlers in parallel, so a slow earlier search can finish last and overwrite newer results. @Action(SearchRooms, { cancelUncompleted: true }) cancels the previous run on each new dispatch; ctx.abortSignal lets async/await code stop too.

open as a page

As the lead of an Angular hotel booking app that needs a store library, how would you weigh NGXS against NgRx, and what does each choice cost the team?

level: principalimportance: should knowfreq 22%
basics
~20 s

NGXS trades NgRx's pure reducers and separate effects for state classes whose DI-enabled @Action handlers update state and do async work in one place. That gives less ceremony and awaitable actions, at the cost of a less explicit, less pure design.

open as a page

In RxJS, what is the difference between of([1, 2, 3]) and from([1, 2, 3]), and what inputs does from() accept?

level: juniorimportance: must knowfreq 70%
basics
~20 s

of([1, 2, 3]) emits the array as one value; from([1, 2, 3]) emits 1, 2 and 3 separately. from() converts any ObservableInput - arrays, iterables, promises, async iterables, ReadableStreams or Observables - into an Observable.

open as a page

In RxJS, how does an Observable differ from a Promise in when work starts, how many values it delivers, and cancellation?

level: juniorimportance: must knowfreq 80%
basics
~20 s

An RxJS Observable is lazy: its producer runs only when subscribed, once per subscriber, and can deliver zero, one or many values synchronously or asynchronously. A Promise starts eagerly, settles once, and has no cancel; unsubscribe() stops an Observable's work.

open as a page

In RxJS, what does distinctUntilChanged() drop from a sensor stream, and why can the same reading still appear twice?

level: juniorimportance: must knowfreq 64%
basics
~20 s

distinctUntilChanged() drops a value only when it equals the last value it emitted, using === by default. It remembers one key, so 21, 21, 22, 21 becomes 21, 22, 21; distinct() is the operator that suppresses every earlier repeat.

open as a page

In RxJS, why should a typeahead search use switchMap rather than mergeMap to call the search API for each query?

level: juniorimportance: must knowfreq 76%
basics
~20 s

Search responses can return out of order. mergeMap forwards every response, so a slow reply for an old query can overwrite the newest results. switchMap unsubscribes the previous request when a new query arrives, so only the latest results arrive.

open as a page

In RxJS, how do debounceTime and throttleTime differ, and which fits autosave while typing versus a scroll-position tracker?

level: juniorimportance: must knowfreq 72%
basics
~20 s

debounceTime emits the latest value once the stream has been silent for the given time, which suits autosave after typing pauses. throttleTime emits a value, then ignores the source for a fixed window, which suits steady scroll updates.

open as a page

In a Pinia order store, how do you write a synchronous addItem action and an asynchronous placeOrder action, and how does a checkout component call them?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Actions are functions: listed under actions in an option store, where they use this and so cannot be arrows, or returned from a setup store. An async action returns a promise the component awaits; failures reach the caller unless handled.

open as a page

In a Vue 3 app with Pinia 4, what do createPinia(), app.use(pinia) and defineStore('cart', …) each do before a header badge can call useCartStore()?

level: juniorimportance: must knowfreq 72%
basics
~20 s

createPinia() builds the root container, app.use(pinia) installs it so components can find it, and defineStore('cart', …) only returns a useCartStore function; the store is created on its first call and then shared by every component.

open as a page

In a Pinia option store for invoices, how do you define a total-with-tax getter, how do components read it, and when does it recompute?

level: juniorimportance: must knowfreq 64%
basics
~20 s

Add it under getters, for example subtotal: (state) => …, and read invoice.total as a property. Pinia wraps each getter in one computed per store, so all components share a cached value that recomputes only after state it read changes.

open as a page

In a Pinia store for a user-preferences panel, when do you write state directly, call $patch with an object, or call $patch with a function?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Direct writes suit single fields and v-model. $patch with an object applies several fields at once, merging nested objects but replacing arrays. $patch with a function mutates state in place for array edits or logic; both patch forms notify subscribers once.

open as a page

How do you unit-test a Pinia notifications store's actions and getters, and why call setActivePinia(createPinia()) before each test?

level: juniorimportance: must knowfreq 50%
basics
~20 s

Call setActivePinia(createPinia()) in beforeEach, then call useNotificationsStore() directly and exercise real actions and getters. A fresh pinia per test gives a fresh store, because stores are cached per pinia and state would otherwise leak between tests.

open as a page

What is VueUse, and what does calling its useEventListener or useIntersectionObserver in a Vue 3 component save you from writing by hand?

level: juniorimportance: must knowfreq 34%
basics
~20 s

VueUse is a collection of ready-made Vue 3 composables for browser APIs, sensors and reactive state. useEventListener and useIntersectionObserver attach their listener or observer, follow reactive targets, and remove it automatically when the component's scope is disposed.

open as a page

In VueUse, how do refDebounced, useDebounceFn and useThrottleFn differ, and which fits a dashboard's search box versus its resize handler?

level: middleimportance: should knowfreq 30%
basics
~20 s

refDebounced returns a read-only ref that copies a source ref once it stops changing; useDebounceFn wraps a function so it runs after calls pause; useThrottleFn runs a function at most once per interval. Debounce the search box; throttle the resize handler.

open as a page

With VueUse's useStorage persisting a dashboard filter to localStorage, why is a newly added filter field undefined for returning users?

level: middleimportance: should knowfreq 26%
basics
~20 s

useStorage uses the stored value whenever the key exists and ignores the default, so a filter object saved before the field existed lacks it. mergeDefaults: true shallow-merges the defaults under the stored object; a merge function handles nested fields.

open as a page

A dashboard panel calls VueUse's useFetch(`/api/stats?range=${props.range}`) and refDebounced on props.search; why does neither react when the props change, and how should you pass them?

level: seniorimportance: should knowfreq 20%
basics
~20 s

Both calls receive plain values read once during setup. Pass useFetch a getter URL plus refetch: true, since refetch defaults to false; refDebounced accepts only a Ref, so wrap the prop with toRef(() => props.search).

open as a page

Several dashboard panels each call useWindowSize(); what does wrapping it in VueUse's createSharedComposable change on the client, and what happens during SSR?

level: seniorimportance: nice to knowfreq 16%
basics
~20 s

On the client, the first call runs useWindowSize in a detached scope and later calls reuse its refs, so one set of listeners serves all panels until the last caller is disposed. During SSR every call gets a fresh, unshared instance.

open as a page