React Cheatsheet

Context

Use this React reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

What Context Does

Context lets you pass data through the component tree without prop drilling. Any component that calls useContext gets the value without intermediaries needing to forward it.

Use context for global or subtree-wide data: theme, locale, current user, auth state. Avoid it for frequently changing data (every consumer re-renders) — use a state manager instead.

createContext

import { createContext } from 'react';

// Signature: createContext(defaultValue)
// defaultValue is used only when a component has NO Provider above it
const ThemeContext = createContext('light');      // string default
const UserContext  = createContext(null);          // null default
const CountContext = createContext({ count: 0, increment: () => {} });

Provider

Wrap the subtree that needs access to the value.

import { createContext, useState } from 'react';

export const ThemeContext = createContext('light');

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () =>
    setTheme(prev => prev === 'light' ? 'dark' : 'light');

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

React 19 shorthand — render the context object itself as the provider (.Provider still works but is no longer needed):

// React 19+
return (
  <ThemeContext value={{ theme, toggleTheme }}>
    {children}
  </ThemeContext>
);
// Wrap at the appropriate level (root, page, or section)
import { ThemeProvider } from './ThemeContext';

function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
    </ThemeProvider>
  );
}

useContext

Read the context value in any descendant.

import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemeToggle() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button onClick={toggleTheme}>
      Switch to {theme === 'light' ? 'dark' : 'light'} mode
    </button>
  );
}

// The component re-renders whenever the context VALUE changes

use(Context) — Conditional Reads (React 19)

use reads a context like useContext, but it may be called inside conditionals and loops (it must still be inside a component or hook).

import { use } from 'react';
import { ThemeContext } from './ThemeContext';

function Heading({ children, level }) {
  if (level > 3) {
    return <h4>{children}</h4>;      // early return BEFORE reading context — fine
  }
  const theme = use(ThemeContext);   // useContext could not be called here
  return <h1 className={theme}>{children}</h1>;
}

Full Pattern: Context + Custom Hook

Export a custom hook to: - Hide the useContext import detail - Provide a helpful error when used outside the provider

// AuthContext.jsx
import { createContext, useContext, useState } from 'react';

const AuthContext = createContext(null);

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

  const login = async (email, password) => {
    const res = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
      headers: { 'Content-Type': 'application/json' },
    });
    const data = await res.json();
    setUser(data.user);
  };

  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// Custom hook — throws if used outside provider
export function useAuth() {
  const ctx = useContext(AuthContext);
  if (ctx === null) throw new Error('useAuth must be used within AuthProvider');
  return ctx;
}

// Usage in any descendant
function Profile() {
  const { user, logout } = useAuth();
  return (
    <>
      <p>Hello, {user?.name}</p>
      <button onClick={logout}>Log out</button>
    </>
  );
}

Updating Context

The Provider's value is just a prop — updating parent state updates all consumers.

export const CartContext = createContext(null);

export function CartProvider({ children }) {
  const [items, setItems] = useState([]);

  const addItem    = (item)  => setItems(prev => [...prev, item]);
  const removeItem = (id)    => setItems(prev => prev.filter(i => i.id !== id));
  const clearCart  = ()      => setItems([]);
  const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);

  return (
    <CartContext.Provider value={{ items, addItem, removeItem, clearCart, total }}>
      {children}
    </CartContext.Provider>
  );
}

Nesting Multiple Providers

function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <CartProvider>
          <Router />
        </CartProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

Context Without State (Static Values)

// Config, feature flags, etc. — never changes, no re-renders
const FeatureFlagContext = createContext({});

function App() {
  const flags = { newDashboard: true, betaSearch: false };
  return (
    <FeatureFlagContext.Provider value={flags}>
      <Router />
    </FeatureFlagContext.Provider>
  );
}

Default Value Behavior

const UserContext = createContext({ name: 'Guest', role: 'viewer' });

// Component with no Provider ancestor gets the default value directly
function Standalone() {
  const user = useContext(UserContext); // { name: 'Guest', role: 'viewer' }
  return <p>{user.name}</p>;
}

The default value is useful for testing components in isolation — render without a Provider and get predictable defaults.

Optimizing Re-renders

Every consumer re-renders when the value prop changes reference. Avoid creating new objects on each parent render.

// BAD — new object every render triggers all consumers
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    // { theme, setTheme } is a new object each render
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// GOOD — memoize the value object
import { useMemo } from 'react';

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const value = useMemo(
    () => ({ theme, setTheme }),
    [theme]
  );

  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

Split contexts by update frequency — separate read-only state from dispatch to avoid over-rendering:

const StoreStateContext  = createContext(null); // data
const StoreDispatchContext = createContext(null); // actions — stable reference

function StoreProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <StoreDispatchContext.Provider value={dispatch}>
      <StoreStateContext.Provider value={state}>
        {children}
      </StoreStateContext.Provider>
    </StoreDispatchContext.Provider>
  );
}

// Components that only dispatch never re-render on state changes
function DeleteButton({ id }) {
  const dispatch = useContext(StoreDispatchContext);
  return <button onClick={() => dispatch({ type: 'delete', id })}>Delete</button>;
}

Context vs State Management Libraries

Context + useReducerZustand / Jotai / Redux
SetupBuilt-in, no depsExtra package
DevToolsBasicRich time-travel tools
Perf at scaleRe-renders all consumersGranular subscriptions
Use whenLow-freq global stateComplex / high-freq state

Common Built-in Contexts (React)

// Not exported by React — these are examples of commonly built contexts
// React itself only has one built-in context: used by Profiler internally

// Common patterns you'll build yourself:
// ThemeContext    — 'light' | 'dark' + toggle
// AuthContext     — user, login(), logout()
// LocaleContext   — 'en' | 'fr' + translations
// RouterContext   — (used internally by React Router)
// ToastContext    — addToast(msg), removeToast(id)