useContext
useContext
est un Hook React qui vous permet de lire et de vous abonner à un contexte depuis votre composant.
const value = useContext(SomeContext)
Référence
useContext(SomeContext)
Appelez useContext
à la racine de votre composant pour lire et vous abonner au contexte.
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
Voir d’autres exemples plus bas.
Paramètres
SomeContext
: le contexte que vous avez préalablement créé aveccreateContext
. Le contexte lui-même ne contient pas d’information, il représente seulement le type d’information que vous pouvez fournir ou lire depuis des composants.
Valeur renvoyée
useContext
renvoie la valeur du contexte pour le composant qui l’appelle. C’est déterminé par la value
passée par le SomeContext.Provider
le plus proche au-dessus du composant appelant. S’il n’y a pas un tel fournisseur, alors la valeur renvoyée sera la defaultValue
que vous avez passée à createContext
pour ce contexte. La valeur renvoyée est toujours à jour. React refait automatiquement le rendu des composants qui lisent un contexte lorsque ce dernier change.
Limitations
- L’appel à
useContext()
dans un composant n’est pas affecté par les fournisseurs renvoyés par le même composant. Le<Context.Provider>
correspondant doit figurer au-dessus du composant qui appelle le HookuseContext()
. - React fait automatiquement le rendu de tous les enfants qui utilisent un contexte spécifique, en commençant par le fournisseur qui reçoit une
value
différente. La valeur précédente et la suivante sont comparées avecObject.is
. Sauter des rendus avecmemo
n’empêche pas les enfants de recevoir une nouvelle valeur de contexte. - Le contexte peut être cassé si votre système de construction produit des modules dupliqués en sortie (ce qui peut arriver avec les liens symboliques). Passer quelque chose via le contexte ne marche que si le
SomeContext
que vous avez utilisé pour fournir le contexte et leSomeContext
que vous utilisez pour le lire sont exactement le même objet, ce qui est déterminé par une comparaison===
.
Utilisation
Transmettre des données en profondeur dans l’arbre
Appelez useContext
au niveau le plus élevé de votre composant pour lire et vous abonner au contexte.
import { useContext } from 'react';
function Button() {
const theme = useContext(ThemeContext);
// ...
useContext
renvoie la valeur du contexte pour le contexte que vous avez passé. Pour définir la valeur du contexte, React remonte dans l’arbre des composants à la recherche du fournisseur de contexte le plus proche pour ce contexte particulier.
Pour passer un contexte à un Button
, il faut enrober ce composant ou l’un de ses parents dans le fournisseur de contexte correspondant :
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... faire le rendu des boutons à l'intérieur ...
}
Le nombre de couches de composants qu’il y a entre le fournisseur et le Bouton
importe peu. Un Button
situé n’importe où à l’intérieur du Form
reçoit la valeur "dark"
quand il appelle useContext(ThemeContext)
.
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Bienvenue"> <Button>S'inscrire</Button> <Button>Se connecter</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
Mettre à jour les données passées au contexte
Vous voudrez souvent que le contexte change avec le temps. Pour mettre à jour le contexte, associez-le à un état. Déclarez une variable d’état dans le composant parent, et passez l’état actuel au fournisseur en tant que valeur de contexte.
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Passer au thème clair
</Button>
</ThemeContext.Provider>
);
}
Désormais, tout Button
à l’intérieur du fournisseur recevra la valeur actuelle de theme
. Si vous appelez setTheme
pour mettre à jour la valeur de theme
que vous avez passée au fournisseur, tous les Button
referont leurs rendus avec la nouvelle valeur "light"
.
Exemple 1 sur 5 · Mettre à jour une valeur via le contexte
Dans cet exemple, le composant MyApp
contient une variable d’état qui est ensuite passée au fournisseur ThemeContext
. Cocher la case « Utiliser le mode sombre » met à jour cet état. Changer la valeur fournie refait le rendu de tous les composants utilisant ce contexte.
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={theme}> <Form /> <label> <input type="checkbox" checked={theme === 'dark'} onChange={(e) => { setTheme(e.target.checked ? 'dark' : 'light') }} /> Utiliser le mode sombre </label> </ThemeContext.Provider> ) } function Form({ children }) { return ( <Panel title="Bienvenue"> <Button>S'inscrire</Button> <Button>Se connecter</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
Remarquez que value="dark"
passe la chaîne de caractères "dark"
, mais que value={theme}
passe la valeur de la variable JavaScript theme
en utilisant les accolades de JSX. Ces accolades vous permettent également de passer des valeurs de contexte qui ne sont pas des chaînes de caractères.
Spécifier une valeur de secours par défaut
Si React ne trouve aucun fournisseur pour ce contexte particulier dans l’arbre du parent, la valeur de contexte renvoyée par useContext()
sera égale à la valeur par défaut que vous avez spécifiée lorsque vous avez créé ce contexte :
const ThemeContext = createContext(null);
La valeur par défaut ne change jamais. Si vous voulez mettre à jour le contexte, associez-le avec un état comme décrit plus haut.
Vous pouvez souvent utiliser une valeur par défaut plus significative que null
, comme par exemple :
const ThemeContext = createContext('light');
De cette façon, si par inadvertance vous faites le rendu de certains composants sans le bon contexte associé, ça ne cassera pas. Ça permet aussi à votre composant de se comporter correctement dans un environnement de test sans avoir à définir tout un tas de fournisseurs pour les tests.
Dans l’exemple ci-dessous, le bouton « Changer de thème » est toujours en clair, parce qu’il se situe en dehors de tout contexte fournissant le thème, et la valeur par défaut de ce thème est 'light'
. Essayez de changer la valeur par défaut à 'dark'
.
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext('light'); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <> <ThemeContext.Provider value={theme}> <Form /> </ThemeContext.Provider> <Button onClick={() => { setTheme(theme === 'dark' ? 'light' : 'dark'); }}> Changer de thème </Button> </> ) } function Form({ children }) { return ( <Panel title="Bienvenue"> <Button>S'inscrire</Button> <Button>Se connecter</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children, onClick }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className} onClick={onClick}> {children} </button> ); }
Surcharger le contexte pour une partie de l’arbre
Vous pouvez surcharger le contexte pour une partie de l’arbre en l’enrobant avec un fournisseur ayant une valeur différente.
<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>
Vous pouvez imbriquer et surcharger les fournisseurs autant de fois que nécessaire.
Exemple 1 sur 2 · Surcharger un thème
Ici, le bouton à l’intérieur du Footer
reçoit une valeur de contexte différente ("light"
) de celle reçue par les boutons à l’extérieur ("dark"
).
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Bienvenue"> <Button>S'inscrire</Button> <Button>Se connecter</Button> <ThemeContext.Provider value="light"> <Footer /> </ThemeContext.Provider> </Panel> ); } function Footer() { return ( <footer> <Button>Paramètres</Button> </footer> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> {title && <h1>{title}</h1>} {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
Optimiser les rendus lorsqu’on passe des objets et des fonctions
Vous pouvez passer n’importe quelle valeur via le contexte, y compris des objets et des fonctions.
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}
return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}
Ici, la valeur de contexte est un objet JavaScript avec deux propriétés, dont l’une est une fonction. À chaque fois que MyApp
fait son rendu (par exemple lors d’un changement de route), ce sera un objet différent pointant vers une fonction différente, React devra donc refaire le rendu de tous les composants situés en profondeur dans l’arbre qui appellent useContext(AuthContext)
.
Ce n’est pas un problème pour les petites applis. Cependant, il est inutile de faire le rendu si les données sous-jacentes, comme currentUser
, n’ont pas changé. Pour aider React à tirer parti de ça, vous pouvez enrober la fonction login
dans un Hook useCallback
et la création de l’objet dans un Hook useMemo
. C’est une optimisation de performances :
import { useCallback, useMemo } from 'react';
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}
Grâce à ce changement, même si MyApp
a besoin d’un nouveau rendu, les composants appelant useContext(AuthContext)
pourront se l’épargner, à moins que currentUser
ait changé.
Apprenez-en davantage sur useMemo
et useCallback
.
Dépannage
Mon composant ne voit pas la valeur de mon fournisseur
Il y a plusieurs raisons pour ça se produise :
- Vous faites le rendu de
<SomeContext.Provider>
dans le même composant (ou en dessous) que celui où vous appelezuseContext()
. Déplacez<SomeContext.Provider>
au-dessus et en-dehors du composant appelantuseContext()
. - Vous avez peut-être oublié d’enrober votre composant avec
<SomeContext.Provider>
ou vous l’avez placé dans une partie différente de votre arbre que celle que vous imaginiez. Vérifiez si la hiérarchie est correcte en utilisant les outils de développement React. - Il se peut que vous rencontriez un problème de build avec vos outils qui fait que le
SomeContext
vu par le composant fournisseur et leSomeContext
vu par le composant qui le lit sont deux objets différents. Ça peut arriver si vous utilisez des liens symboliques par exemple. Vous pouvez vous en assurer en les affectant à des variables globales commewindow.SomeContext1
etwindow.SomeContext2
et en vérifiant le résultat dewindow.SomeContext1 === window.SomeContext2
dans la console. Si elles sont différentes, corrigez le problème au niveau de l’outil de build.
Je reçois undefined
de mon contexte bien que la valeur par défaut soit différente
Vous avez peut-être un fournisseur sans value
dans l’arbre :
// 🚩 Ça ne marche pas : pas de prop « value »
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>
Oublier de spécifier la value
revient à passer value={undefined}
.
Il se peut également que vous ayez utilisé par erreur un autre nom de prop :
// 🚩 Ça ne marche pas : la prop doit s'appeler « value »
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>
Dans ces deux cas, vous devriez voir un message d’alerte de React dans la console. Pour les corriger, appelez la prop value
:
// ✅ Passer la prop « value »
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>
Notez que la valeur par défaut de votre appel à createContext(defaultValue)
est seulement utilisée s’il n’y a pas d’autre fournisseur correspondant au-dessus. S’il y a un composant <SomeContext.Provider value={undefined}>
quelque part dans l’arbre parent, le composant qui appelle useContext(SomeContext)
recevra undefined
pour la valeur de contexte.