Une introduction à l'API contextuelle de React

Publié: 2022-03-10
Résumé rapide ↬ Dans cet article, vous apprendrez à utiliser l'API contextuelle de React qui vous permet de gérer les états d'application globaux dans vos applications React sans recourir au forage d'accessoires.

Pour ce didacticiel, vous devez avoir une bonne compréhension des crochets. Néanmoins, avant de commencer, je vais brièvement discuter de ce qu'ils sont et des crochets que nous utiliserons dans cet article.

Selon les documents React :

« Les crochets sont un nouvel ajout dans React 16.8. Ils vous permettent d'utiliser l'état et d'autres fonctionnalités de React sans écrire de classe.

C'est essentiellement ce qu'est un crochet React. Cela nous permet d'utiliser l'état, les références et d'autres fonctionnalités de React dans nos composants fonctionnels.

Discutons des deux crochets que nous rencontrerons dans cet article.

Le useState

Le crochet useState nous permet d'utiliser l'état dans nos composants fonctionnels. Un crochet useState prend la valeur initiale de notre état comme seul argument, et il renvoie un tableau de deux éléments. Le premier élément est notre variable d'état et le deuxième élément est une fonction dans laquelle nous pouvons utiliser la mise à jour de la valeur de la variable d'état.

Jetons un œil à l'exemple suivant :

 import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }

Ici, count est notre variable d'état et sa valeur initiale est 0 tandis que setCount est une fonction que nous pouvons utiliser pour mettre à jour la valeur de count.

Le crochet useContext

J'en parlerai plus tard dans l'article, mais ce crochet nous permet essentiellement de consommer la valeur d'un contexte. Ce que cela signifie réellement deviendra plus évident plus tard dans l'article.

Espaces de travail du fil

Les espaces de travail Yarn vous permettent d'organiser la base de code de votre projet à l'aide d'un référentiel monolithique (monorepo). React est un bon exemple de projet open source qui est monorepo et utilise des espaces de travail Yarn pour atteindre cet objectif. Lire un article connexe →

Plus après saut! Continuez à lire ci-dessous ↓

Pourquoi avons-nous besoin de l'API Context ?

Nous voulons créer un composant "basculement de thème" qui bascule entre le mode clair et le mode sombre pour notre application React. Chaque composant doit avoir accès au mode de thème actuel afin qu'il puisse être stylisé en conséquence.

Normalement, nous fournirions le mode de thème actuel à tous les composants via des accessoires et mettrons à jour le thème actuel en utilisant state :

 import React from "react"; import ReactDOM from "react-dom"; function App() { return ( <div> <Text theme= "blue" /> <h1>{theme}</h1> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

Dans l'exemple de code ci-dessus, nous avons créé un composant de texte qui affiche un élément h1 . La couleur de l'élément h1 dépend du mode de thème actuel. Actuellement, le thème est bleu. Nous pouvons basculer entre les thèmes blue et red en utilisant state .

Nous allons créer un état appelé "theme" en utilisant le crochet useState . Le crochet useState renverra la valeur actuelle du thème et une fonction que nous pouvons utiliser pour mettre à jour le thème.

Alors, créons notre état de thème :

 const [theme, setTheme] = React.useState("blue");

Nous ajouterons également un élément de bouton à notre composant App . Ce bouton sera utilisé pour basculer les thèmes et il a besoin d'un gestionnaire d'événements de clic. Alors, écrivons le gestionnaire d'événements click comme ceci :

 const onClickHandler = () => { setTheme(); }

Maintenant, nous voulons définir le nouveau thème sur Red si le thème actuel est Blue , et vice versa. Au lieu d'utiliser une instruction if , un moyen plus pratique de le faire est d'utiliser l'opérateur ternaire en JavaScript.

 setTheme( theme === "red"? "blue": "red");

Alors maintenant, nous avons écrit notre gestionnaire onClick . Ajoutons cet élément de bouton au composant App :

 <button onClick = {onClickHandler}>Change theme</button>

Modifions également la valeur des accessoires de thème du composant Text en état de thème.

 <Text theme={theme}/>

Maintenant, nous devrions avoir ceci :

 import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const[theme, setTheme] = React.useState("red"); const onClickHandler = () => { setTheme( theme === "red"? "blue": "red"); } return ( <div> <Text theme={theme}/> <button onClick = {onClickHandler}>Change theme</button> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

Nous pouvons maintenant basculer entre nos deux thèmes. Cependant, s'il s'agissait d'une application beaucoup plus grande, il serait difficile d'utiliser le thème dans des composants profondément imbriqués et le code deviendrait difficile à manier.

Présentation de l'API de contexte

Permettez-moi de vous présenter l'API Context. Selon la documentation de React :

"Context fournit un moyen de transmettre des données via l'arborescence des composants sans avoir à transmettre manuellement les accessoires à tous les niveaux."

Pour une définition plus approfondie, il vous permet de mettre des données particulières à la disposition de tous les composants dans l'arborescence des composants, quelle que soit la profondeur d'imbrication de ce composant.

Regardons cet exemple :

 const App = () => { return( <ParentComponent theme = "light"/> ); } const ParentComponent = (props) => ( <Child theme = {props.theme} /> ) const Child = (props) => ( <Grandchild theme = {props.theme} /> ) const Grandchild = (props) => ( <p>Theme: {props.theme}</p> )

Dans l'exemple ci-dessus, nous avons spécifié le thème de l'application à l'aide d'un accessoire dans le ParentComponent appelé theme . Nous avons dû transmettre ces accessoires à tous les composants de l'arborescence des composants pour les obtenir là où ils sont nécessaires, à savoir le composant GrandChild . Le ChildComponent n'avait rien à voir avec les accessoires du thème mais était juste utilisé comme intermédiaire.

Maintenant, imaginez que le composant GrandChild soit plus profondément imbriqué que dans l'exemple du haut. Nous aurions à passer les accessoires de thème de la même manière que nous l'avons fait ici, ce qui serait fastidieux. C'est le problème que Context résout. Avec Context , chaque composant de l'arborescence des composants a accès à toutes les données que nous décidons de mettre dans notre contexte.

Commençons par le Context

Il est temps de répliquer le bouton de basculement de thème que nous avons construit au début de l'article avec l'API Context. Cette fois, notre basculeur de thème sera un composant séparé. Nous allons construire un composant ThemeToggler qui change le thème de notre application React en utilisant Context .

Tout d'abord, initialisons notre application React. (Je préfère utiliser create-react-app mais vous pouvez utiliser la méthode que vous préférez.)

Une fois que vous avez initialisé votre projet React, créez un fichier appelé ThemeContext.js dans votre dossier /src . Vous pouvez également créer un dossier appelé /context et y placer votre fichier ThemeContext si vous le souhaitez.

Maintenant, passons à autre chose.

Création de votre API de contexte

Nous allons créer notre contexte de thème dans notre fichier ThemeContext.js .

Pour créer un contexte, nous utilisons React.createContext qui crée un objet de contexte. Vous pouvez passer n'importe quoi comme argument à React.createContext . Dans ce cas, nous allons passer une chaîne qui est le mode du thème courant. Alors maintenant, notre mode de thème actuel est le mode de thème "léger".

 import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;

Pour rendre ce contexte disponible à tous nos composants React, nous devons utiliser un fournisseur. Qu'est-ce qu'un fournisseur ? Selon la documentation React, chaque objet de contexte est livré avec un composant Provider React qui permet aux composants consommateurs de s'abonner aux changements de contexte. C'est le fournisseur qui permet au contexte d'être consommé par d'autres composants. Cela dit, créons notre fournisseur.

Accédez à votre fichier App.js. Afin de créer notre fournisseur, nous devons importer notre ThemeContext .

Une fois le ThemeContext importé, nous devons enfermer le contenu de notre composant App dans les balises ThemeContext.Provider et donner au composant ThemeContext.Provider un accessoire appelé value qui contiendra les données que nous voulons mettre à disposition de notre arborescence de composants.

 function App() { const theme = "light"; return ( <ThemeContext.Provider value = {theme}> <div> </div> </ThemeContext.Provider> ); }

Alors maintenant, la valeur de "lumière" est disponible pour tous nos composants (ce que nous écrirons bientôt).

Création de notre fichier de thème

Maintenant, nous allons créer notre fichier de thème qui contiendra les différentes valeurs de couleur pour nos thèmes clairs et sombres. Créez un fichier dans votre dossier /src appelé Colors.js .

Dans Colors.js , nous allons créer un objet appelé AppTheme . Cet objet contiendra les couleurs de nos thèmes. Une fois que vous avez terminé, exportez l'objet AppTheme comme suit :

 const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;

Il est maintenant temps de commencer à créer nos différents composants React.

Création de nos composants React

Créons les composants suivants :

  • Header
  • ThemeToggler
  • MainWithClass

En-tête.jsx

 import React from "react"; import ThemeToggler from "./ThemeToggler"; const headerStyles = { padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center" } const Header = () => { return( <header style = {headerStyles}> <h1>Context API</h1> <ThemeToggler /> </header> ); } export default Header;

ThemeToggler.jsx

(Pour l'instant, nous renverrons simplement un div vide.)

 import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return( <div style = {themeTogglerStyle}> </div> ); } export default ThemeToggler;

Consommer du contexte avec des composants basés sur des classes

Ici, nous allons utiliser la valeur de notre ThemeContext . Comme vous le savez peut-être déjà, nous avons deux méthodes pour écrire des composants dans React : via des fonctions ou des classes. Le processus de contexte d'utilisation dans les deux méthodes est différent, nous allons donc créer deux composants pour servir de section principale de notre application : MainWithClass et MainWithFunction .

Commençons par MainWithClass .

MainWithClass.jsxMainWithClass.jsx

Nous devrons importer nos ThemeContext et AppTheme . Une fois cela fait, nous allons écrire une classe qui renvoie notre JSX à partir d'une méthode de rendu. Maintenant, nous devons consommer notre contexte. Il existe deux méthodes pour le faire avec des composants basés sur des classes :

  1. La première méthode passe par Class.contextType .

    Pour utiliser cette méthode, nous attribuons l'objet de contexte de notre ThemeContext à la propriété contextType de notre classe. Après cela, nous pourrons accéder à la valeur de contexte en utilisant this.context . Vous pouvez également y faire référence dans l'une des méthodes de cycle de vie et même dans la méthode de rendu.

     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context]; return( <main></main> ); } }

    Après avoir assigné ThemeContext à la propriété contextType de notre classe, j'ai enregistré l'objet thème actuel dans la variable currentTheme .

    Maintenant, nous allons récupérer les couleurs de la variable currentTheme et les utiliser pour styliser certains balisages.
     render() { const currentTheme = AppTheme[this.context]; return ( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main>

    C'est ça! Cette méthode, cependant, vous limite à ne consommer qu'un seul contexte.
  2. La deuxième méthode est ThemeContext.Consumer qui implique l'utilisation d'un Consumer. Chaque objet de contexte est également livré avec un composant Consumer React qui peut être utilisé dans un composant basé sur une classe. Le composant consommateur prend un enfant en tant que fonction et cette fonction renvoie un nœud React. La valeur de contexte actuelle est transmise à cette fonction en tant qu'argument.

    Maintenant, remplaçons le code de notre composant MainWithClass par ceci :
     class Main extends Component { constructor() { super(); this.state = { } } render(){ return( <ThemeContext.Consumer> { (theme) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } }

    Comme vous pouvez le voir, nous avons utilisé la valeur actuelle de notre ThemeContext que nous avons alias "theme" et nous avons saisi les valeurs de couleur pour ce mode de thème et l'avons affectée à la variable currentTheme . Avec cette méthode, vous pouvez utiliser plusieurs Consommateurs.

Ce sont les deux méthodes de consommation de contexte avec des composants basés sur des classes.

Consommer le contexte avec des composants fonctionnels

Consommer du contexte avec des composants fonctionnels est plus facile et moins fastidieux qu'avec des composants basés sur des classes. Pour consommer du contexte dans un composant fonctionnel, nous utiliserons un hook appelé useContext .

Voici à quoi ressemblerait la consommation de notre ThemeContext avec un composant fonctionnel :

 const Main = () => { const theme = useContext(ThemeContext); const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

Comme vous pouvez le voir, tout ce que nous avions à faire était d'utiliser notre hook useContext avec notre ThemeContext passé en argument.

Note : Vous devez utiliser ces différents composants dans le fichier App.js afin de voir les résultats.

Mise à jour de notre thème avec le composant ThemeToggler

Nous allons maintenant travailler sur notre composant ThemeToggler . Nous devons pouvoir basculer entre les thèmes clairs et sombres. Pour ce faire, nous allons devoir éditer notre ThemeContext.js . Notre React.createContext va maintenant prendre un objet ressemblant au résultat d'un hook useState comme argument.

 const ThemeContext = React.createContext(["light", () => {}]);

Nous avons passé un tableau à la fonction React.createContext . Le premier élément du tableau est le mode de thème actuel et le deuxième élément est la fonction qui serait utilisée pour mettre à jour le thème. Comme je l'ai dit, cela ressemble juste au résultat d'un crochet useState mais ce n'est pas exactement le résultat d'un crochet useState .

Nous allons maintenant modifier notre fichier App.js. Nous devons changer la valeur transmise au fournisseur en un crochet useState . Maintenant, la valeur de notre contexte de thème est un hook useState dont la valeur par défaut est "light".

 function App() { const themeHook = useState("light"); return ( <ThemeContext.Provider value = {themeHook}> <div> <Header /> <Main /> </div> </ThemeContext.Provider> ); }

Écrire notre composant ThemeToggler

Écrivons maintenant notre composant ThemeToggler :

 import React,{useContext} from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { const[themeMode, setThemeMode] = useContext(ThemeContext); return( <div style = {themeTogglerStyle} onClick = {() => {setThemeMode(themeMode === "light"? "dark": "light")}}> <span title = "switch theme"> {themeMode === "light" ? "" : "️"} </span> </div> ); } export default ThemeToggler;

Étant donné que la valeur de notre contexte de thème est maintenant un crochet chaque fois que nous appelons useContext dessus, il renverra un tableau. En utilisant la déstructuration, nous avons pu récupérer les éléments du tableau. Nous avons ensuite écrit un gestionnaire d'événements onClick pour notre ThemeToggler . Avec ce code, chaque fois que vous cliquez sur le basculeur de thème, il changera le thème de notre application.

Nous allons maintenant éditer les différentes versions de notre composant Main .

Modification de notre composant MainWithClass

  1. La version du composant MainWithClass qui utilise la méthode Class.contextType :
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context[0]]; return( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } }
  2. La version du composant MainWithClass qui utilise la méthode ThemeContext.Consumer :
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component { constructor() { super(); this.state = {} } render() { return ( <ThemeContext.Consumer> { ([theme]) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } } export default Main;

Modification de notre composant MainWithFunction

Le composant MainWithFunction doit être modifié comme suit :

 import React, { useContext } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; const Main = () => { const theme = useContext(ThemeContext)[0]; const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

Conclusion

C'est ça! Nous avons réussi à implémenter deux modes de thème pour notre application React en utilisant l'API Context.

Dans le processus, nous avons appris :

  • Qu'est-ce que l'API Context et le problème qu'elle résout ;
  • Quand utiliser l'API de contexte ?
  • Créer un Context et le consommer dans des composants fonctionnels et basés sur des classes.

Lectures complémentaires sur SmashingMag :

  • Style dans les applications Web modernes
  • Créer des applications mobiles avec Ionic et React
  • Construire une PWA avec Webpack et Workbox
  • Apprendre à connaître l'API MutationObserver