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éé avec createContext. 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 Hook useContext().
  • 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 avec Object.is. Sauter des rendus avec memo 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 le SomeContext 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).

Piège

useContext() cherche toujours le fournisseur le plus proche au-dessus du composant qui l’appelle. Il cherche vers le haut et ne prend pas en compte les fournisseurs situés dans le composant à partir duquel vous appelez useContext().

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".

Exemples de mises à jour du contexte

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.

Jouez avec ces exemples

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 :

  1. Vous faites le rendu de <SomeContext.Provider> dans le même composant (ou en dessous) que celui où vous appelez useContext(). Déplacez <SomeContext.Provider> au-dessus et en-dehors du composant appelant useContext().
  2. 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.
  3. 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 le SomeContext 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 comme window.SomeContext1 et window.SomeContext2 et en vérifiant le résultat de window.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.