🔗 Le problème : Prop Drilling

Le prop drilling consiste à passer des props à travers plusieurs niveaux de composants intermédiaires qui n'en ont pas besoin, uniquement pour les transmettre à un descendant profond.

// ❌ Prop drilling — theme est passé à chaque niveau
function App() {
  const [theme, setTheme] = useState('dark');
  return <Page theme={theme} setTheme={setTheme} />;
}
function Page({ theme, setTheme }) {
  return <Header theme={theme} setTheme={setTheme} />;
}
function Header({ theme, setTheme }) {
  return <Navbar theme={theme} setTheme={setTheme} />;
}
function Navbar({ theme, setTheme }) {
  return <button onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')}>Toggle</button>;
}
⚠️ Context ne résout pas tout — pour les props directes parent→enfant, les props restent préférables. Réservez Context pour les données vraiment globales : thème, auth, langue.

🏗️ Créer un Context

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

// 1. Créer le context avec une valeur par défaut
const ThemeContext = createContext('dark');

// 2. Créer un Provider personnalisé (bonne pratique)
export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('dark');

  function toggleTheme() {
    setTheme(t => t === 'dark' ? 'light' : 'dark');
  }

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

// 3. Hook personnalisé pour consommer le context
export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error('useTheme doit être dans ThemeProvider');
  return context;
}

🎁 Envelopper l'app avec le Provider

// index.jsx — on enveloppe l'app
import { ThemeProvider } from './ThemeContext';

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

// Page n'a plus besoin de transmettre theme !
function Page() {
  return <Header />;
}

function Header() {
  return <Navbar />;
}

🎣 useContext — consommer le context

// N'importe quel descendant peut accéder au context
function Navbar() {
  const { theme, toggleTheme } = useTheme(); // hook personnalisé

  return (
    <nav style={{
      background: theme === 'dark' ? '#0a0e1a' : '#f0f6fc',
      color: theme === 'dark' ? '#e6edf3' : '#1c1c1c'
    }}>
      <button onClick={toggleTheme}>
        {theme === 'dark' ? '☀️' : '🌙'}
      </button>
    </nav>
  );
}

// Composant profondément imbriqué — toujours accès direct
function BoutonTheme() {
  const { theme, toggleTheme } = useTheme();
  return <button onClick={toggleTheme}>Mode {theme}</button>;
}

🔧 Context Auth — pattern courant

const AuthContext = createContext(null);

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

  async function login(email, password) {
    const userData = await fakeLogin(email, password);
    setUser(userData);
  }

  function logout() {
    setUser(null);
  }

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

export function useAuth() {
  return useContext(AuthContext);
}

// Utilisation — n'importe où dans l'arbre
function ProfilPage() {
  const { user, logout } = useAuth();
  if (!user) return <p>Non connecté</p>;
  return <div>{user.nom} <button onClick={logout}>Déconnexion</button></div>;
}
📝 Exercices R07 ▶ Mini-projet : Theme Switcher 🧠 QCM R07 Suivant : Hooks avancés →