Transmettre des données en profondeur avec le Contexte
Habituellement, vous transmettez des informations d'un composant parent à un composant enfant via les props. Mais passer des props peut devenir verbeux et peu pratique si vous devez les faire passer à travers de nombreux composants intermédiaires, ou si de nombreux composants de votre application ont besoin de la même information.Le Contextepermet au composant parent de rendre certaines informations disponibles à n'importe quel composant dans l'arbre en dessous de lui—peu importe la profondeur—sans les passer explicitement via les props.
Vous apprendrez
- Ce qu'est le « prop drilling »
- Comment remplacer le passage répétitif de props par le contexte
- Les cas d'utilisation courants du contexte
- Les alternatives courantes au contexte
Le problème du passage des props
Passer des propsest un excellent moyen de faire transiter explicitement des données à travers votre arbre d'interface utilisateur vers les composants qui les utilisent.
Mais passer des props peut devenir verbeux et peu pratique lorsque vous devez faire passer une prop profondément dans l'arbre, ou si de nombreux composants ont besoin de la même prop. L'ancêtre commun le plus proche peut être très éloigné des composants qui ont besoin des données, etremonter l'étataussi haut peut conduire à une situation appelée « prop drilling ».
Remonter l'état


Prop drilling


Ne serait-il pas formidable s'il existait un moyen de « téléporter » les données vers les composants de l'arbre qui en ont besoin sans passer par les props ? Avec la fonctionnalité de contexte de React, c'est possible !
Contexte : une alternative au passage des props
Le contexte permet à un composant parent de fournir des données à l'ensemble de l'arbre en dessous de lui. Le contexte a de nombreuses utilisations. Voici un exemple. Considérez ce composantHeadingqui accepte unlevelpour sa taille :
Imaginons que vous souhaitiez que plusieurs titres au sein de la mêmeSectionaient toujours la même taille :
Actuellement, vous passez la proplevelà chaque<Heading>séparément :
Ce serait bien si vous pouviez passer la proplevelau composant<Section>à la place et la retirer du<Heading>. De cette façon, vous pourriez garantir que tous les titres d'une même section aient la même taille :
Mais comment le composant<Heading>peut-il connaître le niveau de sa<Section>la plus proche ?Cela nécessiterait un moyen pour un enfant de « demander » des données depuis un endroit plus haut dans l'arbre.
Vous ne pouvez pas le faire avec les props seules. C'est là que le contexte entre en jeu. Vous allez le faire en trois étapes :
- Créerun contexte. (Vous pouvez l'appeler
LevelContext, puisqu'il concerne le niveau de titre.) - Utiliserce contexte depuis le composant qui a besoin des données. (
HeadingutiliseraLevelContext.) - Fournirce contexte depuis le composant qui spécifie les données. (
SectionfourniraLevelContext.)
Le contexte permet à un parent — même éloigné ! — de fournir certaines données à l'ensemble de l'arbre à l'intérieur de lui.
Utilisation du contexte dans des enfants proches


Utilisation du contexte dans des enfants éloignés


Étape 1 : Créer le contexte
Tout d'abord, vous devez créer le contexte. Vous devrezl'exporter depuis un fichierpour que vos composants puissent l'utiliser :
Le seul argument decreateContextest la valeurpar défaut. Ici,1fait référence au plus grand niveau de titre, mais vous pouvez passer n'importe quel type de valeur (même un objet). Vous verrez l'importance de la valeur par défaut à l'étape suivante.
Étape 2 : Utiliser le contexte
Importez le HookuseContextde React et votre contexte :
Actuellement, le composantHeadinglit lelevelà partir des props :
Au lieu de cela, supprimez la proplevelet lisez la valeur à partir du contexte que vous venez d'importer,LevelContext:
useContextest un Hook. Tout commeuseStateetuseReducer, vous ne pouvez appeler un Hook qu'immédiatement à l'intérieur d'un composant React (pas dans des boucles ou des conditions).useContextindique à React que le composantHeadingsouhaite lire leLevelContext.
Maintenant que le composantHeadingn'a pas de proplevel, vous n'avez plus besoin de passer la prop level àHeadingdans votre JSX comme ceci :
Mettez à jour le JSX pour que ce soit leSectionqui le reçoive à la place :
Pour rappel, voici le balisage que vous essayiez de faire fonctionner :
Remarquez que cet exemple ne fonctionne pas encore tout à fait ! Tous les titres ont la même taille carmême si vousutilisezle contexte, vous ne l'avez pas encorefourni.React ne sait pas où le récupérer !
Si vous ne fournissez pas le contexte, React utilisera la valeur par défaut que vous avez spécifiée à l'étape précédente. Dans cet exemple, vous avez spécifié1comme argument àcreateContext, doncuseContext(LevelContext)retourne1, définissant tous ces titres comme<h1>. Résolvons ce problème en faisant en sorte que chaqueSectionfournisse son propre contexte.
Étape 3 : Fournir le contexte
Le composantSectionrend actuellement ses enfants :
Enveloppez-les avec un fournisseur de contextepour leur fournir leLevelContext :
Cela indique à React : « si un composant à l'intérieur de cette<Section>demande leLevelContext, donnez-lui celevel. » Le composant utilisera la valeur du<LevelContext>le plus proche dans l'arborescence de l'interface utilisateur au-dessus de lui.
C'est le même résultat que le code original, mais vous n'avez pas eu besoin de passer la proplevelà chaque composantHeading! Au lieu de cela, il « détermine » son niveau de titre en demandant à laSectionla plus proche au-dessus :
- Vous passez une prop
levelau composant<Section>. Sectionenveloppe ses enfants dans<LevelContext value={level}>.Headingdemande la valeur la plus proche deLevelContextau-dessus avecuseContext(LevelContext).
Utiliser et fournir un contexte depuis le même composant
Actuellement, vous devez encore spécifier manuellement lelevelde chaque section :
Puisque le contexte vous permet de lire des informations depuis un composant parent, chaqueSectionpourrait lire lelevelde laSectionau-dessus, et passer automatiquementlevel + 1vers le bas. Voici comment vous pourriez le faire :
Avec ce changement, vous n'avez plus besoin de passer la proplevel non plus au <Section>ni au<Heading>:
Maintenant,HeadingetSectionlisent tous deux leLevelContextpour déterminer à quelle "profondeur" ils se trouvent. Et le composantSectionenveloppe ses enfants dans leLevelContextpour spécifier que tout ce qui est à l'intérieur se trouve à un niveau "plus profond".
Remarque
Cet exemple utilise les niveaux de titres car ils montrent visuellement comment les composants imbriqués peuvent écraser le contexte. Mais le contexte est utile pour de nombreux autres cas d'utilisation. Vous pouvez transmettre n'importe quelle information nécessaire à l'ensemble du sous-arbre : le thème de couleur actuel, l'utilisateur actuellement connecté, etc.
Le contexte traverse les composants intermédiaires
Vous pouvez insérer autant de composants que vous le souhaitez entre le composant qui fournit le contexte et celui qui l'utilise. Cela inclut à la fois les composants intégrés comme<div>et les composants que vous pourriez créer vous-même.
Dans cet exemple, le même composantPost(avec une bordure en pointillés) est rendu à deux niveaux d'imbrication différents. Notez que le<Heading>à l'intérieur obtient automatiquement son niveau de la<Section>la plus proche :
Vous n’avez rien fait de spécial pour que cela fonctionne. UnSectionspécifie le contexte pour l’arbre qu’il contient, donc vous pouvez insérer un<Heading>n’importe où, et il aura la bonne taille. Essayez-le dans le bac à sable ci-dessus !
Le contexte vous permet d’écrire des composants qui « s’adaptent à leur environnement » et s’affichent différemment selonoù(ou, en d’autres termes,dans quel contexte) ils sont rendus.
Le fonctionnement du contexte peut vous rappelerl’héritage des propriétés CSS.En CSS, vous pouvez spécifiercolor: bluepour un<div>, et tout nœud DOM à l’intérieur, quelle que soit sa profondeur, héritera de cette couleur, à moins qu’un autre nœud DOM intermédiaire ne la remplace parcolor: green. De même, dans React, la seule façon de remplacer un contexte provenant d’un niveau supérieur est d’envelopper les enfants dans un fournisseur de contexte avec une valeur différente.
En CSS, différentes propriétés commecoloretbackground-colorne se remplacent pas mutuellement. Vous pouvez définir la<div>de tous lescoloren rouge sans affecter lebackground-color. De même,les différents contextes React ne se remplacent pas mutuellement.Chaque contexte que vous créez aveccreateContext()est complètement séparé des autres, et relie les composants qui utilisent et fournissentce contexte particulier. Un composant peut utiliser ou fournir de nombreux contextes différents sans problème.
Avant d’utiliser le contexte
Le contexte est très tentant à utiliser ! Cependant, cela signifie aussi qu’il est trop facile d’en abuser.Le simple fait que vous ayez besoin de faire passer certaines props sur plusieurs niveaux ne signifie pas que vous devriez mettre ces informations dans un contexte.
Voici quelques alternatives que vous devriez envisager avant d’utiliser le contexte :
- Commencez parpasser des props.Si vos composants ne sont pas triviaux, il n’est pas inhabituel de faire passer une douzaine de props à travers une douzaine de composants. Cela peut sembler fastidieux, mais cela rend très clair quels composants utilisent quelles données ! La personne qui maintient votre code sera ravie que vous ayez rendu le flux de données explicite avec des props.
- Extrayez des composants etpassez du JSX comme enfantsà ceux-ci.Si vous faites passer certaines données à travers de nombreuses couches de composants intermédiaires qui n’utilisent pas ces données (et les font seulement passer plus bas), cela signifie souvent que vous avez oublié d’extraire certains composants en cours de route. Par exemple, vous pouvez passer des props de données comme
postsà des composants visuels qui ne les utilisent pas directement, comme<Layout posts={posts} />. Au lieu de cela, faites queLayoutprennechildrencomme prop, et rendez<Layout><Posts posts={posts} /></Layout>. Cela réduit le nombre de couches entre le composant qui spécifie les données et celui qui en a besoin.
Si aucune de ces approches ne fonctionne bien pour vous, envisagez le contexte.
Cas d’utilisation du contexte
- Thématisation :Si votre application permet à l'utilisateur de changer son apparence (par exemple, mode sombre), vous pouvez placer un fournisseur de contexte en haut de votre application et utiliser ce contexte dans les composants qui doivent ajuster leur apparence visuelle.
- Compte actuel :De nombreux composants peuvent avoir besoin de connaître l'utilisateur actuellement connecté. Le placer dans un contexte permet de le lire facilement n'importe où dans l'arborescence. Certaines applications vous permettent également d'utiliser plusieurs comptes simultanément (par exemple, pour laisser un commentaire en tant qu'un autre utilisateur). Dans ces cas, il peut être pratique d'encapsuler une partie de l'interface utilisateur dans un fournisseur imbriqué avec une valeur de compte actuel différente.
- Routage :La plupart des solutions de routage utilisent le contexte en interne pour conserver la route actuelle. C'est ainsi que chaque lien « sait » s'il est actif ou non. Si vous construisez votre propre routeur, vous voudrez peut-être faire de même.
- Gestion de l'état :À mesure que votre application grandit, vous pouvez vous retrouver avec beaucoup d'état près du sommet de votre application. De nombreux composants distants en dessous peuvent vouloir le modifier. Il est courant d'utiliser un réducteur avec le contextepour gérer un état complexe et le transmettre à des composants distants sans trop de difficultés.
Le contexte n'est pas limité aux valeurs statiques. Si vous passez une valeur différente au rendu suivant, React mettra à jour tous les composants qui le lisent en dessous ! C'est pourquoi le contexte est souvent utilisé en combinaison avec l'état.
En général, si certaines informations sont nécessaires par des composants distants dans différentes parties de l'arborescence, c'est une bonne indication que le contexte vous aidera.
Récapitulatif
- Le contexte permet à un composant de fournir des informations à toute l'arborescence en dessous de lui.
- Pour transmettre un contexte :
- Créez-le et exportez-le avec
export const MyContext = createContext(defaultValue). - Passez-le au Hook
useContext(MyContext)pour le lire dans n'importe quel composant enfant, quelle que soit sa profondeur. - Encapsulez les enfants dans
<MyContext value={...}>pour le fournir depuis un parent.
- Créez-le et exportez-le avec
- Le contexte traverse tous les composants intermédiaires.
- Le contexte vous permet d'écrire des composants qui « s'adaptent à leur environnement ».
- Avant d'utiliser le contexte, essayez de passer des props ou de passer du JSX comme
children.
Essayez quelques défis
Challenge 1 of 1:Remplacer le prop drilling par un contexte #
Dans cet exemple, cocher la case modifie la prop imageSize passée à chaque <PlaceImage>. L’état de la case est géré par le composant de plus haut niveau App, mais chaque <PlaceImage> doit en être informé.
Actuellement, App passe imageSize à List, qui la passe à chaque Place, qui la passe à PlaceImage. Supprimez la prop imageSize, et passez-la plutôt directement du composant App à PlaceImage.
Vous pouvez déclarer le contexte dans Context.js.
