SmarMaker - Documentation
Docs» 15_training:module3-hooks-fondamentaux:usecontext

Chapitre 4 : useContext

Le problème : Prop Drilling

Quand des données doivent passer à travers plusieurs niveaux de composants :

snippet.javascript
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.

La solution : Context

Context permet de “téléporter” des données à travers l'arbre de composants.

Étape 1 : Créer le Context

snippet.javascript
import { createContext } from 'react';
 
const UserContext = createContext(null);

Étape 2 : Fournir la valeur (Provider)

snippet.javascript
function App() {
    const [user, setUser] = useState({ name: 'Jean' });
 
    return (
        <UserContext.Provider value={user}>
            <Layout />
        </UserContext.Provider>
    );
}

Étape 3 : Consommer la valeur (useContext)

snippet.javascript
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 :

snippet.javascript
function Layout() {
    return (
        <div>
            <Header />
            <Main />
        </div>
    );
}
 
function Header() {
    return <Navbar />;
}
 
function Navbar() {
    return <UserMenu />;
}

Exemple complet : Thème

snippet.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 (
        <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;
}
snippet.javascript
// App.js
import { ThemeProvider } from './ThemeContext';
 
function App() {
    return (
        <ThemeProvider>
            <Layout />
        </ThemeProvider>
    );
}
snippet.javascript
// ThemeToggle.js
import { useTheme } from './ThemeContext';
 
function ThemeToggle() {
    const { theme, toggleTheme } = useTheme();
 
    return (
        <button onClick={toggleTheme}>
            Mode actuel : {theme}
        </button>
    );
}
snippet.javascript
// Card.js
import { useTheme } from './ThemeContext';
 
function Card({ children }) {
    const { theme } = useTheme();
 
    return (
        <div className={`card card-${theme}`}>
            {children}
        </div>
    );
}

Passer des fonctions via Context

Vous pouvez passer des fonctions pour permettre aux enfants de modifier l'état :

snippet.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 (
        <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>;
}

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 :

snippet.javascript
function App() {
    return (
        <AuthProvider>
            <ThemeProvider>
                <LanguageProvider>
                    <Layout />
                </LanguageProvider>
            </ThemeProvider>
        </AuthProvider>
    );
}

Exercices

Exercice 1 : Context de langue

Créer un context pour gérer la langue (fr/en) avec un bouton pour changer.

Solution :

snippet.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 (
        <LanguageContext.Provider value={{ lang, toggleLang, t }}>
            {children}
        </LanguageContext.Provider>
    );
}
 
export function useLanguage() {
    return useContext(LanguageContext);
}
snippet.javascript
// Greeting.js
function Greeting() {
    const { t, toggleLang } = useLanguage();
 
    return (
        <div>
            <h1>{t('greeting')}</h1>
            <button onClick={toggleLang}>{t('button')}</button>
        </div>
    );
}

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)

← Chapitre précédent | Retour au module | Module suivant : React Avancé →

Previous Next

Made with ❤ by CAP-REL · SmartMaker · GNU AGPL v3+
Code source · Faire un don
SmarMaker - Documentation
Traductions de cette page:
  • Français
  • Deutsch
  • English
  • Español
  • Italiano
  • Nederlands

Table of Contents

Table des matières

  • Chapitre 4 : useContext
    • Le problème : Prop Drilling
    • La solution : Context
      • Étape 1 : Créer le Context
      • Étape 2 : Fournir la valeur (Provider)
      • Étape 3 : Consommer la valeur (useContext)
    • Exemple complet : Thème
    • Passer des fonctions via Context
    • Quand utiliser Context ?
    • Context vs Props
    • Plusieurs Contexts
    • Exercices
      • Exercice 1 : Context de langue
    • Points clés à retenir
    • Récapitulatif du Module 3
  • SmartAuth
  • SmartMaker - Back (PHP)
    • Mapping Dolibarr - React
  • SmartMaker - Front (React)
    • Animations de pages
    • Architecture
    • Astuces
    • Calendar
    • Composants et pages
    • Configuration du Provider
    • Debug et Logs
    • Hooks SmartCommon
    • PWA (Progressive Web App)
    • Requêtes API
    • Routage
    • SmartCommon
    • Stockage de données
    • Thèmes
    • Traductions
  • HowTo - Pas à pas - Votre première application
    • Développement PHP (back)
    • Développement React (front)
    • Première étape : Module Builder Dolibarr
    • SmartAuth
    • SmartBoot : Un "squelette" quasiment prêt à l'emploi
  • Formation SmartMaker
    • Module 1 : Fondamentaux JavaScript ES6+
      • Chapitre 1 : Variables et Scope
      • Chapitre 2 : Fonctions
      • Chapitre 3 : Programmation Asynchrone
      • Chapitre 4 : Modules ES6
    • Module 2 : Introduction à React
      • Chapitre 1 : Philosophie React
      • Chapitre 2 : JSX
      • Chapitre 3 : Composants
    • Module 3 : Hooks React Fondamentaux
      • Chapitre 1 : useState
      • Chapitre 2 : useEffect
      • Chapitre 3 : useRef
      • Chapitre 4 : useContext
    • Module 4 : React Avancé
      • Chapitre 1 : useCallback et useMemo
      • Chapitre 2 : Custom Hooks
      • Chapitre 3 : Redux et Redux Toolkit
    • Module 5 : Architecture SmartMaker
      • Chapitre 1 : Structure du projet
      • Chapitre 2 : Configuration
      • Chapitre 3 : Flux de données
    • Module 6 : SmartCommon - Composants
      • Chapitre 1 : Mise en page
      • Chapitre 2 : Navigation
      • Chapitre 3 : Formulaires
      • Chapitre 4 : Affichage
    • Module 7 : SmartCommon - Hooks
      • Chapitre 1 : useApi
      • Chapitre 2 : Gestion d'état
      • Chapitre 3 : Hooks utilitaires
    • Module 8 : Backend API (PHP)
      • Chapitre 1 : Routage
      • Chapitre 2 : Controllers
      • Chapitre 3 : Mappers
      • Extrafields et formulaires dynamiques
    • Module 9 : Intégration complète
      • Chapitre 1 : Backend
      • Chapitre 2 : Frontend
      • Chapitre 3 : Déploiement
    • Module 10 : Fonctionnalités avancées
      • Chapitre 1 : Mode offline
      • Chapitre 2 : Internationalisation (i18n)
      • Chapitre 3 : Autres fonctionnalités
    • Module 11 : Bonnes pratiques
  • Démonstration
  • Start
  • Composants et pages
  • Afficher le texte source
  • Anciennes révisions
  • Liens de retour
  • Haut de page