SyntaxStudy
Sign Up
React useContext and Custom Context Hooks
React Beginner 1 min read

useContext and Custom Context Hooks

useContext accepts a context object returned by createContext and returns the current context value. The call is equivalent to reading the nearest Provider value above in the component tree. If the context value changes, React re-renders every component that calls useContext with that context. Exposing context through a custom hook rather than calling useContext directly in components provides two benefits. First, it co-locates the context import with the hook, so consumers never have to import the raw context object. Second, you can add a guard that throws a descriptive error when the hook is used outside its Provider, catching configuration mistakes during development rather than at runtime with a confusing undefined error. When the context value is an object, memoising it with useMemo prevents all consumers from re-rendering whenever the Provider's parent re-renders for unrelated reasons.
Example
import { createContext, useContext, useState, useMemo } from 'react';

// ── Auth context ───────────────────────────────────────────────────────────
const AuthContext = createContext(null);

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

    const value = useMemo(() => ({
        user,
        login:  (userData) => setUser(userData),
        logout: ()         => setUser(null),
    }), [user]);

    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

// ── Custom hook with guard ─────────────────────────────────────────────────
export function useAuth() {
    const ctx = useContext(AuthContext);
    if (ctx === null) {
        throw new Error('useAuth must be used inside <AuthProvider>');
    }
    return ctx;
}

// ── Consumer ───────────────────────────────────────────────────────────────
function Navbar() {
    const { user, logout } = useAuth();
    return (
        <nav>
            {user ? (
                <>
                    <span>Hello, {user.name}</span>
                    <button onClick={logout}>Log out</button>
                </>
            ) : (
                <span>Not logged in</span>
            )}
        </nav>
    );
}