useContext lit une valeur fournie par un composant parent : un composant profond dans l'arbre peut ainsi la recevoir sans que chaque composant intermédiaire la passe en prop. Vous créez un contexte avec createContext, enveloppez une partie de l'arbre dans un provider avec une value, et appelez useContext(TheContext) partout où vous avez besoin de cette valeur.
Toolbar ne mentionne jamais le thème, et pourtant les deux boutons changent quand vous cochez la case. C'est tout l'intérêt du contexte : les composants intermédiaires ne transportent pas une prop qu'ils n'utilisent pas, ce qui est le problème du prop drilling que le contexte résout.
Les trois étapes
// 1. Create it once, outside any component, usually in its own file
export const ThemeContext = createContext('light');
// 2. Provide a value to a part of the tree
<ThemeContext value={theme}>
<Page />
</ThemeContext>
// 3. Read it in any component inside that part
const theme = useContext(ThemeContext);
Dans React 19, l'objet contexte lui-même est le provider. L'ancien code écrit <ThemeContext.Provider value={theme}>, qui fonctionne toujours, vous verrez donc les deux.
useContext remonte l'arbre jusqu'au provider le plus proche de ce contexte. Un composant lit le plus proche, vous pouvez donc imbriquer des providers pour remplacer une valeur dans une section.
La valeur par défaut
L'argument de createContext est ce que useContext renvoie quand aucun provider ne se trouve au-dessus du composant. Elle ne change pas quand l'état change ; c'est une valeur de repli.
Le premier Greeting n'a aucun provider au-dessus de lui et reçoit la valeur par défaut 'en'. Le deuxième lit 'es', et le troisième lit le 'ja' plus proche. Remplacez le value="es" extérieur par value="ja" et seule la deuxième ligne change.
Modifier le contexte depuis un enfant
Le contexte ne fait que transmettre une valeur vers le bas. Pour permettre à un enfant profond de la modifier, gardez la valeur dans l'état là où se trouve le provider, et placez le setter dans le contexte à côté d'elle.
LoginButton appelle setUser, l'état de App change, le provider reçoit une nouvelle valeur, et Header se met aussi à jour. Aucun des deux composants n'a reçu de prop.
Chaque consommateur refait son rendu quand la valeur change
Quand la value d'un provider change, React refait le rendu de chaque composant qui appelle useContext pour ce contexte, même si ce composant est dans un memo et que ses props n'ont pas changé. React compare l'ancienne et la nouvelle valeur avec Object.is. Donc value={{ user, setUser }} est un nouvel objet à chaque rendu de App, et chaque consommateur refait son rendu même quand user n'a pas changé.
Les deux lecteurs écrivent une fois au premier rendu. Cliquez sur le bouton : seul PlainReader écrit à nouveau, car son contexte a reçu un nouvel objet. useMemo donne à MemoContext le même objet tant que name ne change pas, donc MemoReader ne bouge pas. Cela ne compte qu'une fois le rendu devenu lent ; pour une petite application, un nouvel objet par rendu ne pose aucun problème.
Séparer les contextes
Une valeur qui change souvent et une valeur qui ne change jamais ne doivent pas partager un contexte. Si le setter vit avec l'état, un composant qui a seulement besoin d'appeler setUser refait quand même son rendu chaque fois que user change. Placez-les dans deux contextes et chaque consommateur ne s'abonne qu'à ce qu'il lit :
const UserContext = createContext(null);
const SetUserContext = createContext(() => {});
function UserProvider({ children }) {
const [user, setUser] = useState(null);
return (
<SetUserContext value={setUser}>
<UserContext value={user}>{children}</UserContext>
</SetUserContext>
);
}
Le setUser issu de useState garde la même identité pour toujours, donc les composants qui ne lisent que SetUserContext ne refont jamais leur rendu à cause d'une connexion. La même idée s'applique aux données sans rapport : un thème et un panier d'achat vont dans des contextes séparés, pas dans un gros AppContext.
Une touche finale courante est un hook personnalisé comme useUser() qui appelle useContext(UserContext) et lève une erreur claire quand le provider manque, pour que les appelants n'importent jamais directement l'objet contexte.
Contexte, props ou bibliothèque d'état
- Les props sont le choix par défaut. Passer une valeur deux ou trois niveaux plus bas est clair et facile à suivre.
- Le contexte convient aux valeurs dont de nombreux composants ont besoin à de nombreuses profondeurs et qui changent rarement : thème, langue, utilisateur connecté, objet de feature flags.
- Une bibliothèque d'état (Redux Toolkit, Zustand, Jotai et d'autres) convient à un état volumineux qui se met à jour souvent, quand vous voulez qu'un composant s'abonne à une seule partie et ignore les rendus causés par le reste. Le contexte n'a pas un tel sélecteur : un consommateur refait son rendu à chaque changement de la valeur.
Avant de recourir au contexte ou à une bibliothèque, vérifiez si passer des composants via children supprime le drilling. C'est souvent le cas, sans aucune nouvelle API.
Questions fréquentes
Que fait useContext dans React ?
Il renvoie la valeur actuelle d'un contexte, prise dans le provider le plus proche au-dessus du composant dans l'arbre. Quand la valeur de ce provider change, chaque composant qui la lit refait son rendu.
À quoi sert la valeur par défaut de createContext ?
C'est ce que useContext renvoie quand il n'y a aucun provider au-dessus du composant. Elle est utile pour les tests et pour les composants affichés seuls, et elle ne change jamais.
Ai-je encore besoin de Context.Provider dans React 19 ?
Non. Dans React 19, vous pouvez afficher le contexte lui-même comme provider : <ThemeContext value="dark">. <ThemeContext.Provider value="dark"> fonctionne toujours, donc l'ancien code continue de tourner.
Comment modifier la valeur d'un contexte depuis un composant enfant ?
Gardez la valeur dans l'état du composant qui affiche le provider, et placez le setter dans le contexte à côté de la valeur : value={{ theme, setTheme }}. L'enfant lit setTheme avec useContext et l'appelle.
Le contexte React remplace-t-il Redux ?
Le contexte transmet une valeur vers le bas de l'arbre ; il ne gère pas l'état à lui seul. Combiné à useState ou useReducer, il suffit à de nombreuses applications. Une bibliothèque d'état ajoute ce qui manque au contexte, comme l'abonnement à une seule partie de l'état pour que des mises à jour sans rapport ne refassent pas le rendu d'un composant, plus des devtools et des middlewares.