Skillversitas

useContext Hook

Context API Recap

Context allows data to be passed through the component tree without props drilling.

import React, { createContext, useContext, useState } from 'react';

Creating a Context

const ThemeContext = createContext('light');

The Provider

A Provider wraps the components that need access to the context.

function App() {
  const [theme, setTheme] = useState('light');

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

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

Using useContext

function Toolbar() {
  return <ThemedButton />;
}

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

  return (
    <button onClick={toggleTheme} className={`btn-${theme}`}>
      Theme: {theme}
    </button>
  );
}

Avoiding Prop Drilling

// Without context: prop drilling
function App() {
  const [user, setUser] = useState(null);
  return <Layout user={user} />; // Must pass the prop
}

function Layout({ user }) {
  return <Sidebar user={user} />; // Must pass it again
}

function Sidebar({ user }) {
  return <UserInfo user={user} />; // Must pass it again
}

// With context
const UserContext = createContext(null);

function App() {
  const [user, setUser] = useState(null);
  return (
    <UserContext.Provider value={user}>
      <Layout />
    </UserContext.Provider>
  );
}

// In any component
function UserInfo() {
  const user = useContext(UserContext);
  return <p>Hello, {user?.name}</p>;
}

Example: Theme Context (Dark/Light Mode)

const ThemeContext = createContext();

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

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

  const styles = {
    light: { bg: '#fff', color: '#000' },
    dark: { bg: '#333', color: '#fff' }
  };

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

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

User Auth Context

const AuthContext = createContext();

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

  const login = async (credentials) => {
    const res = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials)
    });
    const data = await res.json();
    setUser(data.user);
    setToken(data.token);
  };

  const logout = () => {
    setUser(null);
    setToken(null);
  };

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

function Navbar() {
  const { user, logout } = useContext(AuthContext);

  return (
    <nav>
      {user ? (
        <>
          <span>Hello, {user.name}</span>
          <button onClick={logout}>Log Out</button>
        </>
      ) : (
        <a href="/login">Log In</a>
      )}
    </nav>
  );
}

Context is very useful for data that is shared globally. Next we will study useRef to access the DOM and store mutable values.