# 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é →]]