Quand des données doivent passer à travers plusieurs niveaux de composants :
function App() { const [user, setUser] = useState({ name: 'Jean' }); return <Layout user={user} />; } function Layout({ user }) { return ( <div> <Header user={user} /> <Main user={user} /> </div> ); } function Header({ user }) { return <Navbar user={user} />; } function Navbar({ user }) { return <UserMenu user={user} />; // Enfin utilisé ici ! } function UserMenu({ user }) { return <span>{user.name}</span>; }
Layout et Header ne font que transmettre user sans l'utiliser. C'est le prop drilling.
Context permet de “téléporter” des données à travers l'arbre de composants.
import { createContext } from 'react'; const UserContext = createContext(null);
function App() { const [user, setUser] = useState({ name: 'Jean' }); return ( <UserContext.Provider value={user}> <Layout /> </UserContext.Provider> ); }
import { useContext } from 'react'; function UserMenu() { const user = useContext(UserContext); return <span>{user.name}</span>; }
Les composants intermédiaires n'ont plus besoin de passer user :
function Layout() { return ( <div> <Header /> <Main /> </div> ); } function Header() { return <Navbar />; } function Navbar() { return <UserMenu />; }
// ThemeContext.js import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(null); 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> ); } export function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme must be used within ThemeProvider'); } return context; }
// App.js import { ThemeProvider } from './ThemeContext'; function App() { return ( <ThemeProvider> <Layout /> </ThemeProvider> ); }
// ThemeToggle.js import { useTheme } from './ThemeContext'; function ThemeToggle() { const { theme, toggleTheme } = useTheme(); return ( <button onClick={toggleTheme}> Mode actuel : {theme} </button> ); }
// Card.js import { useTheme } from './ThemeContext'; function Card({ children }) { const { theme } = useTheme(); return ( <div className={`card card-${theme}`}> {children} </div> ); }
Vous pouvez passer des fonctions pour permettre aux enfants de modifier l'état :
const AuthContext = createContext(null); function AuthProvider({ children }) { const [user, setUser] = useState(null); const login = async (credentials) => { const response = await api.login(credentials); setUser(response.user); }; const logout = () => { setUser(null); }; return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); } function useAuth() { return useContext(AuthContext); } // Utilisation function LoginButton() { const { user, login, logout } = useAuth(); if (user) { return <button onClick={logout}>Déconnexion</button>; } return <button onClick={() => login({ email, password })}>Connexion</button>; }
Bons cas d'usage :
Mauvais cas d'usage :
| Critère | Props | Context |
|---|---|---|
| Données concernent peu de composants | ✅ | ❌ |
| Données traversent beaucoup de niveaux | ❌ | ✅ |
| Flux de données explicite | ✅ | ❌ |
| Configuration globale | ❌ | ✅ |
Vous pouvez imbriquer plusieurs Providers :
function App() { return ( <AuthProvider> <ThemeProvider> <LanguageProvider> <Layout /> </LanguageProvider> </ThemeProvider> </AuthProvider> ); }
Créer un context pour gérer la langue (fr/en) avec un bouton pour changer.
Solution :
// LanguageContext.js import { createContext, useContext, useState } from 'react'; const LanguageContext = createContext(null); const translations = { fr: { greeting: 'Bonjour', button: 'Anglais' }, en: { greeting: 'Hello', button: 'French' } }; export function LanguageProvider({ children }) { const [lang, setLang] = useState('fr'); const toggleLang = () => { setLang(prev => prev === 'fr' ? 'en' : 'fr'); }; const t = (key) => translations[lang][key]; return ( <LanguageContext.Provider value={{ lang, toggleLang, t }}> {children} </LanguageContext.Provider> ); } export function useLanguage() { return useContext(LanguageContext); }
// Greeting.js function Greeting() { const { t, toggleLang } = useLanguage(); return ( <div> <h1>{t('greeting')}</h1> <button onClick={toggleLang}>{t('button')}</button> </div> ); }
| Hook | Usage | Déclenche un re-rendu |
|---|---|---|
| useState | État local | Oui |
| useEffect | Effets de bord (API, events) | Non (mais peut modifier l'état) |
| useRef | DOM et valeurs persistantes | Non |
| useContext | Données globales | Oui (si la valeur change) |
← Chapitre précédent | Retour au module | Module suivant : React Avancé →