React - Bibliothèque Javascript pour le frontend

Auteur: Mohamed CHINY Durée necessaire pour le cours de React - Bibliothèque Javascript pour le frontend Niveau recommandé pour le cours de React - Bibliothèque Javascript pour le frontend Supports vidéo disponibles pour ce cours Exercices de renforcement non disponibles pour ce cours Quiz non disponibles pour ce cours

Page 15: Communication entre les composants à l'aide de React Context

Toutes les pages

Partager des valeurs entre les composants en utilisant le contexte

Le problème de Prop Drilling

Imaginez que l’on souhaite afficher une notification qui renferme un message et un bouton d’action. Si on souhaite profiter au maximum des composants afin de pouvoir les réutiliser ailleurs, nous allons séparer le bouton d’action dans un composant à part. Donc, on aura trois fichiers de base pour notre projet:
  • App.js : qui implémente le composant principale <App /> de notre application.
  • Notification.js : qui décrit le composant <Notification /> et qui sera importé dans le composant <App />.
  • Bouton.js : qui fait office du composant <Bouton /> et qui sera à son tour importé dans le composant <Notification />.

On suppose que les fichiers Notification.js et Bouton.js sont placés dans le dossier components utilisé précédemment.
Imaginons que nous voulons rendre dynamique l'étiquette du bouton, c'est à dire que l'on spécifiera sa valeur dans le composant <App /> et que l'on réutilisera ensuite dans le composant <Bouton />. Nous allons donc faire appel aux props comme ceci:

Code du composant <App />:
import Notification from './components/Notification'

function App() {
   return (
      <>
         <Notification label="Continuer"/>
      </>
   );
}

export default App;

Code du composant <Notification />:
import Bouton from "./Bouton"

function Notification(props){
   return (
      <>
         <h2>Notification</h2>
         <div>Cliquez sur le bouton pour continuer</div>
         <hr />
         <Bouton label={props.label} />
      </>
   )
}

export default Notification
Code du composant <Bouton />:
function Bouton(props){
   return(
      <>
         <button>{props.label}</button>
      </>
   )
}

export default Bouton
Vous avez constaté que l'étiquette du bouton (message "Continuer") a été passé comme un prop nommé label lors de l'invocation du composant <Notification /> au sein du composant <App />. Ce même prop a été retransmis comme prop lors de l'appel du composant <Bouton /> au sein du composant <Notification />.

Vous avez compris que le composant <Notification /> n'a pas vraiment besoin de cette étiquette, mais on doit le lui transmettre quand même car il figure au milieu de la chaîne entre <App /> et <Bouton />. Donc, si ça se trouve que notre chaîne de composants est plus longue que ça (un composant qui appelle un autre), alors on se retrouvera avec nos props qui se baladent un peu partout. Ce phénomène est ce que l'on appelle Prop Drilling.

Partager les valeurs entre un Provider et un Consumer en utilisant le mécanisme du contexte

Pour contrer le problème de Prop Drilling, un mécanisme nommé Context a été intégré à React. Son principe est simple, on mettra en place deux entités nommées Provider (fournisseur) et Consumer (Client ou consommateur). Il s'agit en fait de deux composants qui sont créés automatiquement avec le mécanisme Context. D'après leurs noms, le Provider va créer et initialiser les valeurs à partager entre les composants, et le Consumer va avoir accès à ces valeurs là depuis n'importe quel composant de la chaîne. Autrement dit, on n'a plus besoin de trainer nos valeurs (props) dans tous les composants comme on l'a fait avant. Le composant qui en a besoin peut y accéder directement.
Le terme Consumer fait référence à une ancienne méthode utilisée en React. Les versions actuelles utilisent des méthodes bien plus simples et efficaces comme on le verra juste après.

La fonction createContext et le hook useContext

La fonction createContext, comme son nom l'indique, permet de créer un contexte, c'est à dire, les données globales qui seront partagées à travers l'arborescence des composants imbriqués et qui seront accessibles à n'importe quel niveau de cette arborescence là. Cette fonction est généralement utilisée dans le composant de plus haut niveau (<App /> dans notre cas).

La fonction createContext crée un provider qui servira à envelopper les composants de l'applications qui sont susceptibles d'utiliser les données partagées. Il s'agit là d'une sorte de wrapper qui spécifie la valeur à communiquer.

Le code du composant <App /> ressemblera à ceci:
import { createContext } from 'react';
import Notification from './components/Notification'
export const label = createContext(null)

function App() {
   return (
      <>
         <label.Provider value="Continuer">
            <Notification />
         </label.Provider>
      </>
   );
}

export default App;
Premièrement, on importe le module createContext de la bibliothèque react. Ensuite, on crée le contexte à l'aide la fonction createContext(). On peut spécifier une valeur initiale du contexte. Dans ce cas, j'ai simplement mis null. Le contexte créé est placé dans un objet que j'ai appelé label.

Une chose importante à noter, c'est qu'il faut exporter l'objet contexte qui vient d'être créé. De cette façon, on pourra l'importer depuis n'importe quel autre composant.
Il ne faut pas exporter le contexte par défaut car une seule exportation par défaut est permise par composant.
Par la suite, on spécifie le wrapper Provider qu'on préfixe par le nom du contexte (<label.Provider>). Il doit envelopper tous les composants enfants dont les descendants sont susceptibles d'utiliser le contexte partagé. Comme valeur, on met ce qu'on veut. Dans mon cas, j'ai juste spécifié la chaîne de caractères "Continuer" qui fera office de l'étiquette du bouton.

Maintenant, on se place dans le composant qui aura besoin d'utiliser la donnée partagée par le contexte. Dans notre cas, il s'agit du composant <Bouton /> dont le code ressemblera à ceci:
import { useContext } from "react"
import { label } from '../App'

function Bouton(){
   const labelValue = useContext(label)
   return(
      <>
      <button>{labelValue}</button>
      </>
   )
}

export default Bouton

On importe l'objet label qui représente le contexte partagé. N'oubliez pas de le placer entre accolades car son exportation n'était pas pas défaut.

On invoque le hook useContext qui accepte comme argument l'objet label. Assurez-vous d'avoir importé le module useContext de la bibliothèque react avant.

La variable labelValue générée à laide du hook useContext contient la donnée partagée par le Provider, c'est à dire la chaîne de caractères "Continuer" qu'on affiche tout simplement en guise d'étiquette du bouton en utilisant l'interpolation JSX.