100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

What Are the Main State Management Patterns in Frontend Apps?

Learn local, shared, global store, and server state patterns in frontend apps, and when to use each one correctly.

mediumQ81 of 224 in Web Development Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

Frontend state management patterns fall into a spectrum from local component state, through lifted/shared state via props and context, to centralized global stores (Redux/Zustand-style) and server-state caching libraries, and the right choice depends on whether the data is UI-local, shared-but-app-owned, or actually owned by a remote server.

Local state (useState/useReducer) is correct for anything only one component and its direct children need, like a form field's current value or a toggle's open/closed flag. When multiple distant components need the same value, "lifting state up" to a common ancestor and passing it down, or using Context to avoid prop drilling, works for low-frequency updates like theme or authenticated user. For complex, frequently-changing, cross-cutting application state, a centralized store (Redux, Zustand, Jotai) enforces a single source of truth with predictable, traceable updates, often via a reducer pattern (action in, new state out) that makes debugging and time-travel easier at the cost of more boilerplate. Server state β€” data fetched from an API β€” is a fundamentally different category from client state because it is asynchronous, can go stale, and is owned elsewhere; libraries like React Query or SWR handle caching, refetching, and invalidation for that specific shape of problem rather than forcing it into a generic global store. The key architectural skill is correctly classifying each piece of state into local vs shared-client vs server-state, because using the wrong pattern (e.g., putting server data in Redux with manual cache invalidation) creates unnecessary complexity.

  • Local state keeps components simple and avoids unnecessary re-renders elsewhere
  • Context avoids prop drilling for low-frequency, widely-needed values
  • Centralized stores give a single source of truth and predictable, traceable updates
  • Dedicated server-state libraries handle caching/staleness without reinventing that logic

AI Mentor Explanation

Local state is like a batsman privately tracking his own strike rate on his wristband β€” nobody else needs it. Shared state is like the scoreboard operator relaying the team total, something the whole ground needs visibility into without every player independently tracking it. A centralized store is like the official scorer's single ledger that all updates must funnel through so no two boards ever disagree. Server state is like the live TV broadcast feed itself β€” owned by the network, refreshed on its own schedule, and the ground staff only display a cached copy of it.

Step-by-Step Explanation

  1. Step 1

    Classify the state

    Decide if it is local-only, shared client state, or server-owned data.

  2. Step 2

    Pick the smallest sufficient tool

    useState/useReducer for local; Context for low-frequency shared values.

  3. Step 3

    Escalate to a store when needed

    Use Redux/Zustand/Jotai for frequently-changing, cross-cutting client state needing traceability.

  4. Step 4

    Handle server state separately

    Use React Query/SWR for caching, refetching, and invalidating remote data instead of manual sync.

What Interviewer Expects

  • Ability to distinguish local, shared-client, and server state as different problems
  • Knowledge of when Context becomes a performance liability (frequent updates causing wide re-renders)
  • Understanding of the reducer pattern (action β†’ new state) for predictable updates
  • Awareness of why server-state libraries exist separately from generic global stores

Common Mistakes

  • Putting all state in a global store β€œjust in case,” adding unnecessary boilerplate
  • Using Context for high-frequency updates, causing broad, expensive re-renders
  • Treating fetched API data as permanent client state instead of cacheable, staleness-prone server state
  • Prop-drilling deeply instead of lifting state or using Context appropriately

Best Answer (HR Friendly)

β€œState management is about deciding where a piece of data should live: something only one small component cares about stays local, something several components need gets shared through context or a small store, and data that actually comes from the server is handled with tools built specifically for caching and refreshing it, rather than treated the same as regular app state.”

Code Example

Local vs shared vs server state in one component
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useCartStore } from './store' // e.g. Zustand

function ProductCard({ productId }) {
  // Local state: only this card cares about hover
  const [isHovered, setIsHovered] = useState(false)

  // Shared client state: cart is needed across many components
  const addToCart = useCartStore((s) => s.addToCart)

  // Server state: fetched, cached, and revalidated automatically
  const { data: product, isLoading } = useQuery({
    queryKey: ['product', productId],
    queryFn: () => fetch(`/api/products/${productId}`).then((r) => r.json()),
  })

  if (isLoading) return <Spinner />

  return (
    <div onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <h3>{product.name}</h3>
      <button onClick={() => addToCart(product)}>Add to Cart</button>
    </div>
  )
}

Follow-up Questions

  • When would you reach for Redux/Zustand instead of Context alone?
  • How does React Query decide when cached server data is stale?
  • What causes unnecessary re-renders with Context, and how do you mitigate it?
  • How would you structure state for a form with 50+ fields?

MCQ Practice

1. Why is fetched API data usually treated differently from regular client state?

Server state has staleness and ownership characteristics that generic client state tools do not address well.

2. What is a common downside of overusing React Context for frequently-changing values?

Every consumer of a Context re-renders when its value changes, which is costly for high-frequency updates.

3. What core pattern do libraries like Redux use for predictable state updates?

Reducers take the current state and an action and return a new state, making updates traceable and predictable.

Flash Cards

When to use local state? β€” When only one component and its children need the value.

When does Context become risky? β€” When it updates frequently, causing wide re-renders in all consumers.

Why treat server state separately? β€” It is async, can go stale, and is owned by a remote source, unlike client-owned state.

What does a reducer pattern provide? β€” Predictable, traceable state updates via action-in, new-state-out.

1 / 4

Continue Learning