Q&A
What is global state with React?
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
- Local
useState- keep state as local as possible (the default; don't globalize prematurely). - Lift state up - when a few sibling components share it.
- Context - app-wide, low-frequency data (theme, auth, user).
- Redux/Zustand/etc. - large apps, complex/shared state, frequent updates, need for devtools/middleware.
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
- Reaching for Redux/global state for everything, including data that should be local.
- Putting rapidly-changing values in Context, causing widespread re-renders.
- Treating cached server data as global UI state instead of using React Query/SWR.
- One giant global store that couples unrelated features.
- Forgetting Context consumers re-render on any value change (memoize/split contexts).
References
- React Context: https://react.dev/learn/passing-data-deeply-with-context
- Redux Toolkit: https://redux-toolkit.js.org/ · Zustand: https://github.com/pmndrs/zustand