---
title: "Chapitre 4 : useContext"
weight: 430
---

# 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 <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

```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 (
        <UserContext.Provider value={user}>
            <Layout />
        </UserContext.Provider>
    );
}
```

### Étape 3 : Consommer la valeur (useContext)

```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` :

```javascript
function Layout() {
    return (
        <div>
            <Header />
            <Main />
        </div>
    );
}

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

function Navbar() {
    return <UserMenu />;
}
```

## 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 (
        <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;
}
```

```javascript
// App.js
import { ThemeProvider } from './ThemeContext';

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

```javascript
// ThemeToggle.js
import { useTheme } from './ThemeContext';

function ThemeToggle() {
    const { theme, toggleTheme } = useTheme();

    return (
        <button onClick={toggleTheme}>
            Mode actuel : {theme}
        </button>
    );
}
```

```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 :

```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 :

```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 :**

```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);
}
```

```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](/training/module3-hooks-fondamentaux/useref) | [Retour au module](/training/module3-hooks-fondamentaux) | [Module suivant : React Avancé ->](/training/module4-react-avance)
