iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

useContext

useContext reads a value from a Context Provider higher in the tree, without prop-drilling. Use for theme, auth user, i18n, anything that’s genuinely app-wide.

Provider + consumer + custom hook

EXAMPLE
import { createContext, useContext, useState, useMemo } from 'react';

// 1) Create context with a sensible default
const ThemeContext = createContext({ theme: 'light', toggle: () => {} });

// 2) Provider wraps the tree
function ThemeProvider({ children }) {
    const [theme, setTheme] = useState('light');
    const value = useMemo(
        () => ({ theme, toggle: () => setTheme((t) => (t === 'light' ? 'dark' : 'light')) }),
        [theme],
    );
    return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}

// 3) Consumer via useContext
function Button() {
    const { theme, toggle } = useContext(ThemeContext);
    return <button onClick={toggle}>Theme: {theme}</button>;
}

// 4) App
export default function App() {
    return (
        <ThemeProvider>
            <header><Button /></header>
            <main>…</main>
        </ThemeProvider>
    );
}

// 5) Custom hook — friendlier API + runtime error
function useTheme() {
    const ctx = useContext(ThemeContext);
    if (!ctx) throw new Error('useTheme must be used inside ThemeProvider');
    return ctx;
}

// 6) Real-world: auth context
const AuthContext = createContext(null);

export function AuthProvider({ children }) {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        api.me().then(setUser).finally(() => setLoading(false));
    }, []);

    const value = useMemo(
        () => ({
            user,
            loading,
            signIn:  async (email, pw) => setUser(await api.signIn(email, pw)),
            signOut: async () => { await api.signOut(); setUser(null); },
        }),
        [user, loading],
    );
    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export const useAuth = () => useContext(AuthContext);

// 7) Multiple contexts — compose
<AuthProvider>
    <ThemeProvider>
        <I18nProvider>
            <App />
        </I18nProvider>
    </ThemeProvider>
</AuthProvider>

// 8) Performance — every consumer re-renders on value change
// Mitigations:
//   • useMemo the value object
//   • Split context into smaller contexts (state vs dispatch)
//   • Use a state-management library (Zustand, Jotai) for hot paths

const StateContext    = createContext();
const DispatchContext = createContext();

function CounterProvider({ children }) {
    const [state, dispatch] = useReducer(reducer, initial);
    return (
        <StateContext.Provider value={state}>
            <DispatchContext.Provider value={dispatch}>{children}</DispatchContext.Provider>
        </StateContext.Provider>
    );
}

// Components that only DISPATCH don't rerender when state changes.

// 9) Selector pattern — read only what you need
// Native context doesn't support selectors; libs do (use-context-selector).
// Or just split context.

// 10) When NOT to use context
//   • Server state (TanStack Query / SWR — has cache + revalidation)
//   • Frequent updates (Zustand, Jotai, Redux Toolkit)
//   • Local-to-one-component state (just useState)

// 11) Common bugs
//   • Forgetting <Provider> wrapping → consumers get default value silently
//   • value={{ ... }} inline → new object each render → every consumer rerenders
//   • Calling context functions during render → infinite loops
//   • Storing functions in context without useCallback → reference changes

Why it matters

useContext shines for app-wide values that rarely change (auth, theme, i18n). For frequent updates, split contexts or reach for Zustand/Jotai — React’s context model rerenders every consumer on every change.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
const Theme = createContext('light');
function Toolbar() {
    const theme = useContext(Theme);
    return <div className={theme} />;
}
Try it Yourself »

Discussion

Loading…