Irbisa · Cheatsheet

State Management

Local vs global vs server state, Context and its re-render cost, Redux Toolkit, Zustand, Jotai, URL as state, choosing a store

Middle Developer · 21 questions

  1. 01

    What is the difference between local, global and server state in a React app?

    Local state belongs to one component or a small subtree (an open dropdown, an input value), global client state is app-wide data the client owns (theme, session, cart), and server state is a cached copy of data the backend owns, which can go stale at any moment.

  2. 02

    Explain the one-way data flow in Redux. Why must reducers be pure?

    The UI dispatches an action (a plain object that describes what happened), the store runs the reducer (state, action) => newState, and subscribed components re-read their part through selectors.

  3. 03

    Is React Context a replacement for Redux or another state manager?

    No. Context is a dependency injection mechanism: it passes a value down the tree but does not store, update or optimize anything.

  4. 04

    Find the bug: typing in the search box re-renders every component that reads the current user.

    export function AppProvider({ children }: { children: React.ReactNode }) {  const [user, setUser] = useState<User | null>(null);  const [query, setQuery] = useState('');   return (    <AppContext value={{ user, setUser, query, setQuery }}>      {children}    </AppContext>  );} function Avatar() {  const { user } = use(AppContext);  return <img src={user?.avatarUrl} alt="" />;}

    The search query and the user share one context, and the value is a new object on every render, so each keystroke changes the context value and every consumer, including Avatar, re-renders although user did not change.

  5. 05

    What does Redux Toolkit solve compared with hand-written Redux?

    Redux Toolkit is the official way to write Redux: configureStore sets up DevTools, thunks and development checks in one call, and createSlice generates the action creators and the reducer from one object, with Immer allowing mutating syntax.

    import { configureStore, createSlice, type PayloadAction } from '@reduxjs/toolkit';import { useDispatch, useSelector } from 'react-redux'; const cartSlice = createSlice({  name: 'cart',  initialState: { items: [] as { id: string; qty: number }[] },…
  6. 06

    Find the bug: dispatching todosCleared runs the reducer, but the list stays on screen.

    const todosSlice = createSlice({  name: 'todos',  initialState: { items: [] as Todo[], filter: 'all' as Filter },  reducers: {    todoAdded(state, action: PayloadAction<Todo>) {      state.items.push(action.payload);    },    todosCleared(state) {      state = { items: [], filter: state.filter };    },  },});

    The reducer reassigns the state parameter instead of changing the draft or returning a new value.

  7. 07

    How do useSelector subscriptions work, and why can a selector cause extra re-renders?

    useSelector runs the selector after every dispatched action and re-renders the component only if the result changed by reference (===).

    import { createSelector } from '@reduxjs/toolkit';import { useSelector } from 'react-redux'; const selectItems = (state: RootState) => state.todos.items;const selectFilter = (state: RootState) => state.todos.filter;…
  8. 08

    Where do async logic and side effects live in Redux Toolkit?

    Not in reducers.

    import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; export const fetchUser = createAsyncThunk('user/fetch', async (id: string, { signal }) => {  const res = await fetch(`/api/users/${id}`, { signal });  if (!res.ok) throw new Error(`HTTP ${res.status}`);  return (await res.json()) as User;…
  9. 09

    What is RTK Query, and when would you pick it over TanStack Query?

    RTK Query is the data-fetching and caching layer built into Redux Toolkit: you describe endpoints in createApi, it generates hooks, and the cache lives in the Redux store.

    import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const api = createApi({  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),  tagTypes: ['Todo'],  endpoints: (build) => ({…
  10. 10

    How do you store relational or list data in Redux, and what does createEntityAdapter give you?

    Store collections normalized: each entity exactly once in an entities map keyed by id, an ids array for order, and references to other entities by id.

    import { createEntityAdapter, createSlice, type PayloadAction } from '@reduxjs/toolkit'; interface Post {  id: string;  title: string;  authorId: string; // a reference, not a nested user object…
  11. 11

    What is Zustand, and how does it differ from Redux Toolkit?

    Zustand is a small hook-based store: create returns a hook, state and actions live in one object, and components subscribe with a selector.

    import { create } from 'zustand'; interface CartState {  items: string[];  add: (id: string) => void;  clear: () => void;…
  12. 12

    Find the bug: after upgrading to Zustand 5, this component crashes with "Maximum update depth exceeded".

    function CartSummary() {  const { items, clear } = useCart((s) => ({ items: s.items, clear: s.clear }));  return (    <div>      {items.length} items      <button onClick={clear}>Clear</button>    </div>  );}

    The selector returns a new object on every call.

  13. 13

    What is the atomic approach to state (Jotai), and when does it fit better than one store?

    In Jotai, state is many small atoms instead of one tree: a component subscribes to exactly the atoms it reads, and derived atoms compute from other atoms like spreadsheet cells.

    import { atom, useAtom, useAtomValue } from 'jotai'; const priceAtom = atom(100);const qtyAtom = atom(1);const totalAtom = atom((get) => get(priceAtom) * get(qtyAtom)); // derived, read-only…
  14. 14

    What is MobX, and how does its observable model differ from Redux?

    MobX makes state observable, records which observables each component reads during render, and re-renders exactly those components when the values change.

    class CartStore {  items: { id: string; price: number; qty: number }[] = [];   constructor() {    makeAutoObservable(this); // fields: observable, getters: computed, methods: actions  }…
  15. 15

    How do you persist store state across page reloads, and what can go wrong?

    Use the store's persistence middleware, such as Zustand's persist or redux-persist: it saves selected state to localStorage (or IndexedDB) and restores it on start.

    import { create } from 'zustand';import { persist } from 'zustand/middleware'; type Theme = 'light' | 'dark'; interface PrefsState {…
  16. 16

    When should state live in the URL instead of in React state or a store?

    Put state in the URL when a user would expect to share it, bookmark it or go back to it: search queries, filters, sorting, pagination, the selected tab or the open item.

  17. 17

    Why is copying API responses into Redux or Context usually an anti-pattern, and what do you use instead?

    Server data is a cache of something the backend owns, so it needs loading and error states, deduplication, refetching, invalidation and garbage collection.

  18. 18

    How do you keep a store maintainable in a large app with many features and teams?

    Split it by feature: each feature owns its slice, selectors and actions in its own folder, other features use only its public selectors, and slices are added when the feature's code loads (combineSlices with lazy slices in Redux Toolkit 2, or separate small stores in Zustand).

  19. 19

    How would you migrate a large app from legacy Redux (connect, sagas, hand-written reducers) to Redux Toolkit or TanStack Query step by step?

    Incrementally, never as a big-bang rewrite: first swap createStore for configureStore, so all old code runs unchanged in the new store, then move server data to RTK Query or TanStack Query, convert reducers to createSlice as features are touched, and retire connect and sagas last.

    import { configureStore } from '@reduxjs/toolkit';import createSagaMiddleware from 'redux-saga';import { legacyReducers } from './legacy/reducers'; // hand-written reducers, untouchedimport { rootSaga } from './legacy/sagas';import { cartSlice } from './features/cart/cartSlice'; // migrated to createSliceimport { api } from './services/api'; // RTK Query, filled endpoint by endpoint…
  20. 20

    When does a state machine (XState) model UI state better than booleans in a store, and what does it cost?

    When a flow has several steps and events that are valid only in some of them: a checkout, an upload with retry and cancel, a connection that reconnects.

    // flags: { isSubmitting, isError, isSuccess } allow 8 combinations, only 4 make sense type State = 'idle' | 'submitting' | 'failed' | 'succeeded';type Event = 'SUBMIT' | 'RESOLVE' | 'REJECT' | 'RESET'; // one state at a time, and each state lists the events it accepts…
  21. 21

    How would you choose a state management approach for a new React app?

    Classify the state first, then pick the lightest tool per category: local state in components, server state in a query cache, shareable view state in the URL, and a store only for the client-owned global state that remains.