Skip to main content

Global state and performance

What "global state" is

Global state is data that different components from different parts of the application can access, for example:

  • the current user,
  • the theme (dark/light),
  • authorization,
  • the shopping cart,
  • application settings, and so on.

This data is usually stored in:

  • the React Context API (useContext);
  • Redux / Zustand / Jotai / MobX;
  • global store singletons.

The problem: excessive global state

When a developer starts putting everything into a global store, even things needed by only one component, a chain reaction of re-renders and added complexity appears in the application.


Why this hurts performance

1. Every global state update triggers mass re-renders

By default, React notifies every consumer of the context (or store) that reads it, even if it doesn't use the field that changed.

javascript
const ThemeContext = createContext(); function App() { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Header /> <Main /> <Footer /> </ThemeContext.Provider> ); }

If you change theme, → every component using useContext(ThemeContext) gets re-rendered, even if it doesn't display the theme directly.

The more components are tied to the context, the more unnecessary updates you get.


2. State loses its "locality"

If everything is stored in one big store, any update → triggers subscribers across the whole tree.

For example, changing the "search query" in a product list shouldn't touch the "user profile panel", but if everything is in one Redux slice, both get recomputed.


3. You can't memoize effectively

When data is global, it's hard to isolate a component:

javascript
const user = useSelector((state) => state.user);

→ Any change in state.user triggers a re-render in React-Redux, even if the component only uses user.name.

If there are dozens of such "small" subscriptions, a "cascade of re-renders" begins.


4. Contexts don't support partial updates

React Context does not support diffing by value. If anything at all changes in the value, every useContext consumer gets a new object → re-render.

javascript
<Provider value={{ theme, user }}>...</Provider>

→ even if only user changed, a component that uses only theme still re-renders, because the object { theme, user } is new by reference.


5. Cognitive load grows

The more global state there is, the harder it is to understand:

  • what is stored where;
  • who updates it;
  • which components depend on what.

This leads to unpredictable side effects, and optimization becomes harder than the business logic itself.


6. Problems with concurrent rendering

In React 18, with concurrent mode enabled, React can:

  • pause a render;
  • roll back changes;
  • re-render part of the tree.

A large global store makes this more expensive, because React is forced to copy and keep snapshots of a larger amount of data.


Example (illustrated)

javascript
// everything in a global context - a bad practice const GlobalContext = createContext(); function App() { const [theme, setTheme] = useState('light'); const [cart, setCart] = useState([]); const [search, setSearch] = useState(''); const value = { theme, cart, search, setTheme, setCart, setSearch }; return ( <GlobalContext.Provider value={value}> <Header /> // uses only theme <Search /> // uses only search <Cart /> // uses only cart </GlobalContext.Provider> ); }

If you change search,

  • Header and Cart still re-render, because value (the object) changed by reference.

Solutions (how to do it right)

1. Split your contexts / stores Create separate providers for independent parts:

javascript
<ThemeProvider> <CartProvider> <SearchProvider> <App /> </SearchProvider> </CartProvider> </ThemeProvider>

Each context updates in isolation.


2. Keep local state local If the data is only needed by one component (or its children), use useState directly in it, rather than in Redux/Context.

javascript
function SearchBar() { const [query, setQuery] = useState(''); ... }

3. Use selective subscriptions If you use Redux / Zustand, use selectors and shallow comparison to subscribe only to the fields you need.

javascript
const price = useSelector(state => state.cart.totalPrice);

→ It will only change if totalPrice changes.


4. Use memoization and split components

  • Wrap heavy components in React.memo;
  • Pass handlers through useCallback;
  • Split a component so that global data doesn't drag the whole tree along.

5. For complex state, use useContextSelector (or Jotai / Zustand) React 19 will add Context Selectors natively. For now you can use use-context-selector: it lets you subscribe to a specific value in a context instead of the whole object.


Summary

ReasonWhy it's harmful
Mass re-rendersAll subscribers update even for a small change
Poor isolationAny change triggers the whole tree
Heavy snapshotsIncreases memory use and GC work
Hard to understand dependenciesIncreases the chance of bugs
No targeted optimization possibleContext doesn't diff by field

The right mindset:

"Keep only what's necessary in global state. Keep everything else local, as close as possible to where it's used."

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.