Main Context problem — all consumers re-render when any value changes. This is by design (React cannot know which consumers use which parts of the context value), but it causes performance issues in large apps.
How it works: When the Provider's value prop changes, React re-renders every component that calls useContext for that context — even if the component only uses a small part of the value.
Solution 1: Split contexts (recommended for most cases):
1// ❌ Bad: one context for everything2const AppContext = createContext({ theme, user, language, notifications });34// ✅ Good: split by concern5const ThemeContext = createContext("light");6const UserContext = createContext<User | null>(null);7const LanguageContext = createContext("en");8const NotificationContext = createContext<Notification[]>([]);910// Now language change does NOT re-render components using theme/user.
Solution 2: Memoize the context value:
1function App() {2 const [theme, setTheme] = useState("light");3 const [user, setUser] = useState<User | null>(null);45 // ❌ New object every render → every consumer re-renders6 const value = { theme, setTheme, user, setUser };78 // ✅ Memoized object → only re-renders consumers when theme changes9 const value = useMemo(10 () => ({ theme, setTheme, user, setUser }),11 [theme, user]12 );1314 return (15 <AppContext.Provider value={value}>16 {children}17 </AppContext.Provider>18 );19}
Solution 3: Selective context (library — use-context-selector):
1import { createContext, useContextSelector } from "use-context-selector";23const UserContext = createContext({ name: "", age: 0, email: "" });45function UserName() {6 // Re-renders ONLY when name changes7 const name = useContextSelector(UserContext, ctx => ctx.name);8 return <span>{name}</span>;9}1011function UserAge() {12 // Re-renders ONLY when age changes13 const age = useContextSelector(UserContext, ctx => ctx.age);14 return <span>{age}</span>;15}1617function UserEmail() {18 // Re-renders ONLY when email changes19 const email = useContextSelector(UserContext, ctx => ctx.email);20 return <span>{email}</span>;21}
Solution 4: Use Zustand or Jotai instead of Context (for performance-critical state):
1import { create } from "zustand";23interface AppState {4 theme: string;5 user: User | null;6 setTheme: (theme: string) => void;7 setUser: (user: User | null) => void;8}910const useAppStore = create<AppState>((set) => ({11 theme: "light",12 user: null,13 setTheme: (theme) => set({ theme }),14 setUser: (user) => set({ user })15}));1617// Only re-renders when theme changes18function ThemeToggle() {19 const theme = useAppStore(state => state.theme);20 const setTheme = useAppStore(state => state.setTheme);21 return (22 <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>23 Current: {theme}24 </button>25 );26}
When to use each approach:
use-context-selector — when many components consume different parts of the same context.Rule of thumb: If context updates frequently (every keystroke, every second) and has many consumers, split it or use a state management library instead.