# Chapitre 4 : useContext ## Le problème : Prop Drilling Quand des données doivent passer à travers plusieurs niveaux de composants : ```javascript function App() { const [user, setUser] = useState({ name: 'Jean' }); return ; } function Layout({ user }) { return (
); } function Header({ user }) { return ; } function Navbar({ user }) { return ; // Enfin utilisé ici ! } function UserMenu({ user }) { return {user.name}; } ``` `Layout` et `Header` ne font que transmettre `user` sans l'utiliser. C'est le **prop drilling**. ## La solution : Context Context permet de "téléporter" des données à travers l'arbre de composants. ### Étape 1 : Créer le Context ```javascript import { createContext } from 'react'; const UserContext = createContext(null); ``` ### Étape 2 : Fournir la valeur (Provider) ```javascript function App() { const [user, setUser] = useState({ name: 'Jean' }); return ( ); } ``` ### Étape 3 : Consommer la valeur (useContext) ```javascript import { useContext } from 'react'; function UserMenu() { const user = useContext(UserContext); return {user.name}; } ``` Les composants intermédiaires n'ont plus besoin de passer `user` : ```javascript function Layout() { return (
); } function Header() { return ; } function Navbar() { return ; } ``` ## Exemple complet : Thème ```javascript // 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 ( {children} ); } export function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme must be used within ThemeProvider'); } return context; } ``` ```javascript // App.js import { ThemeProvider } from './ThemeContext'; function App() { return ( ); } ``` ```javascript // ThemeToggle.js import { useTheme } from './ThemeContext'; function ThemeToggle() { const { theme, toggleTheme } = useTheme(); return ( ); } ``` ```javascript // Card.js import { useTheme } from './ThemeContext'; function Card({ children }) { const { theme } = useTheme(); return (
{children}
); } ``` ## Passer des fonctions via Context Vous pouvez passer des fonctions pour permettre aux enfants de modifier l'état : ```javascript 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 ( {children} ); } function useAuth() { return useContext(AuthContext); } // Utilisation function LoginButton() { const { user, login, logout } = useAuth(); if (user) { return ; } return ; } ``` ## Quand utiliser Context ? **Bons cas d'usage** : - Thème (dark/light mode) - Utilisateur connecté - Langue / internationalisation - Configuration globale **Mauvais cas d'usage** : - État local d'un formulaire - Données qui ne concernent que quelques composants proches - État qui change très fréquemment (performance) ## Context vs Props ^ Critère ^ Props ^ Context ^ | Données concernent peu de composants | ✅ | ❌ | | Données traversent beaucoup de niveaux | ❌ | ✅ | | Flux de données explicite | ✅ | ❌ | | Configuration globale | ❌ | ✅ | ## Plusieurs Contexts Vous pouvez imbriquer plusieurs Providers : ```javascript function App() { return ( ); } ``` ## Exercices ### Exercice 1 : Context de langue Créer un context pour gérer la langue (fr/en) avec un bouton pour changer. **Solution :** ```javascript // 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 ( {children} ); } export function useLanguage() { return useContext(LanguageContext); } ``` ```javascript // Greeting.js function Greeting() { const { t, toggleLang } = useLanguage(); return (

{t('greeting')}

); } ``` ## Points clés à retenir 1. **Context** évite le prop drilling 2. **createContext** → **Provider** → **useContext** 3. Créer un **custom hook** (useTheme, useAuth) pour simplifier l'usage 4. Utiliser pour des données **vraiment globales** 5. Ne pas abuser : les props restent souvent plus claires ## Récapitulatif du Module 3 ^ 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) | [[:15_training:module3-hooks-fondamentaux:useref|← Chapitre précédent]] | [[:15_training:module3-hooks-fondamentaux:start|Retour au module]] | [[:15_training:module4-react-avance:start|Module suivant : React Avancé →]]