DEV Community

Cover image for ReactJs Performance ~ State Management~
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

ReactJs Performance ~ State Management~

・The choice of state management can have a dramatic effect on React performance, but most comparisons focus on developer experience rather than runtime characteristics.

Having implemented Context, Redux, Zustand and Jotai in production apps, the following factors were found to be important: re-render frequency, serialisation overhead and selector performance. Each solution balances these factors differently.

Tool My Impression Trade-off Good Use Case
Context API Simple but limited at scale Re-renders can spread easily Local or tightly related state
Redux Structured and predictable More setup and concepts Large apps with strong debugging needs
Zustand Lightweight and practical Less opinionated structure Modern apps that value simplicity
Jotai Flexible and fine-grained Requires understanding atom-based thinking Derived and modular state
Recoil Good for dependent state Heavier choice for some teams Complex state relationships
MobX Powerful and reactive Different mental model Observable-style architecture

A pitfall for Context API:
When using the Context API, updating a single value (like mode) will cause all components consuming the context to re-render.

import { createContext, useState } from 'react';

const UIContext = createContext(undefined);

export function UIProvider({ children }) {
  const [theme, setTheme] = useState('dark');
  const [account, setAccount] = useState(null);

  // Even a small change like "theme" will trigger re-render for all consumers
  const state = {
    theme,
    updateTheme: setTheme,
    account,
    updateAccount: setAccount,
  };

  return (
    <UIContext.Provider value={state}>
      {children}
    </UIContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Solution: Split contexts according to their update frequency:

// Contexts split by how often they change
const AccountContext = createContext(null);      // Rare updates
const AppearanceContext = createContext(null);   // Medium updates
const AlertContext = createContext(null);        // Frequent updates

// Each component subscribes only to the data it actually needs
function Navbar() {
  const account = useContext(AccountContext); // Re-renders only when account changes

  return <p>Hello, {account?.username}</p>;
}
Enter fullscreen mode Exit fullscreen mode

The Zustand approach offers better performance:

import { create } from 'zustand';

// Components subscribe only to specific slices of state
const useAppStore = create((set) => ({
  account: null,
  colorMode: 'dark',
  messages: [],

  updateAccount: (account) => set({ account }),
  switchMode: (mode) => set({ colorMode: mode }),
  pushMessage: (msg) =>
    set((prev) => ({
      messages: [...prev.messages, msg],
    })),
}));

// This component reacts only to "colorMode"
function ModeSwitcher() {
  const colorMode = useAppStore((s) => s.colorMode);
  const switchMode = useAppStore((s) => s.switchMode);

  return (
    <button onClick={() => switchMode(colorMode === 'dark' ? 'light' : 'dark')}>
      Switch Mode
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)