AlgoInsight

Q&A

What is global state with React?

By AlgoInsight Team2026-06-07frontendreactjavascript

Overview

React state is local by default: a useState value belongs to one component. That's fine for isolated UI, but some data is needed everywhere - authentication, theme, language, a shopping cart. Passing it down through props at every level ("prop drilling") becomes painful. Global state is the pattern of storing such data in one shared place that any component can access directly.

graph TD
    A[Where should state live?] --> B[Local: one component → useState]
    A --> C[Shared by a few → lift state up]
    A --> D[Needed app-wide → global state]
    D --> D1[Context API: built-in]
    D --> D2[Redux / Zustand / Jotai: libraries]
    style D fill:#1e1b4b,color:#fff

The Problem: Prop Drilling

// user must be passed through every intermediate component
<App user={user}>
  <Layout user={user}>
    <Sidebar user={user}>
      <Profile user={user} />   // finally used here
    </Sidebar>
  </Layout>
</App>

Intermediate components don't need user but must forward it. Global state removes this friction.

Solution 1: Context API (built-in)

React's Context provides a value to a subtree without prop drilling:

import { createContext, useContext, useState } from "react";

const UserContext = createContext(null);

function App() {
  const [user, setUser] = useState({ name: "Ada" });
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Layout />
    </UserContext.Provider>
  );
}

function Profile() {
  const { user } = useContext(UserContext);   // read directly, no props
  return <p>{user.name}</p>;
}

Context is ideal for low-frequency global data (theme, auth, locale). Caveat: every consumer re-renders when the context value changes, so it's not optimal for rapidly-changing state.

Solution 2: useReducer + Context (scalable built-in)

For more complex state logic, combine useReducer (centralized update logic) with Context:

const [state, dispatch] = useReducer(reducer, initialState);
// provide { state, dispatch } via Context; components dispatch actions

This is a lightweight Redux-like pattern using only built-ins.

Solution 3: External Libraries

Library Style Notes
Redux (Toolkit) Single store, actions, reducers Powerful, great devtools, more boilerplate; RTK reduces it
Zustand Hook-based store Minimal API, no provider needed, very popular
Jotai / Recoil Atomic state Fine-grained atoms, minimal re-renders
MobX Observable/reactive Mutable, automatic tracking
// Zustand example - tiny global store:
import { create } from "zustand";
const useCart = create(set => ({
  items: [],
  add: item => set(state => ({ items: [...state.items, item] })),
}));
// any component:
const { items, add } = useCart();

Choosing an Approach

A key principle: also distinguish server state (data from APIs) from client/UI state. Tools like React Query / SWR manage server state (caching, refetching) better than a general global store - don't dump everything into one global blob.

Why This Matters in Interviews

React interviews probe whether you understand when state should be global and the trade-offs of each solution. Strong points: identify prop drilling as the problem, know Context (and its re-render caveat), know useReducer+Context as a built-in Redux-lite, and place libraries (Redux, Zustand) by complexity. The mature take: keep state local by default, lift when shared, globalize only when truly app-wide - and separate server state from client state.

Common Pitfalls

References

More answers