169 lines
8.5 KiB
Plaintext
169 lines
8.5 KiB
Plaintext
---
|
|
title: Le hook useContext de React
|
|
description: Découvrez comment utiliser le hook useContext de React pour gérer les contextes dans vos applications.
|
|
tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)]
|
|
---
|
|
|
|
import Callout from "@/components/Callout";
|
|
import tabs from "./tabs";
|
|
|
|
Les contextes sont un moyen de diffuser des données au travers des composants, sans avoir à les passer explicitement à chaque composant.
|
|
|
|
Pour faire simple, imaginons une arborescence de plusieurs composants imbriqués les uns dans les autres :
|
|
|
|
<tabs.reactNestedProps />
|
|
|
|
Fastidieux, n'est-ce pas ? On transmet à chaque fois les mêmes données, et ce, à chaque niveau de l'arborescence.
|
|
|
|
C'est là que les contextes entrent en jeu !
|
|
On va pouvoir alors déclarer notre contexte _(qui contiendra les données à diffuser)_ et le fournir à un niveau supérieur de l'arborescence.
|
|
|
|
## Déclaration d'un contexte
|
|
|
|
Avant de penser à notre contexte, on va réfléchir à ce que l'on veut diffuser et les valeurs par défaut.
|
|
Si on reprend notre exemple avec le thème clair et sombre, on sait que l'on va vouloir diffuser la valeur du thème et une fonction pour le changer.
|
|
|
|
On va donc préparer le terrain en créant un fichier `ThemeContext.jsx` _(ou `ThemeContext.tsx` si tu utilises TypeScript)_ :
|
|
|
|
<tabs.reactCreateContext />
|
|
|
|
## Fournir un contexte
|
|
|
|
Maintenant on peut le dire : notre contexte est prêt à être utilisé !
|
|
Il ne reste plus qu'à le fournir à notre arborescence de composants en lui créant un `Provider`.
|
|
|
|
<Callout type="question" title="Un provider ?">
|
|
Un `Provider` est un composant qui va permettre de **diffuser** les données du contexte à ses enfants.
|
|
Il est important de noter que le `Provider` doit **englober** les composants qui vont utiliser le contexte.
|
|
</Callout>
|
|
|
|
Un contexte React est un objet qui contient deux propriétés : `Provider` et `Consumer`.
|
|
|
|
Le `Provider` est un composant qui va permettre de diffuser les données du contexte à ses enfants.
|
|
Le `Consumer` est un composant qui va permettre de récupérer les données du contexte.
|
|
|
|
<tabs.reactContextProvider />
|
|
|
|
Mais on peut aller encore plus loin, en créant un Provider dédié à notre contexte !
|
|
Cela permettra de simplifier l'arborescence de composants et de rendre le code plus lisible :
|
|
|
|
<tabs.reactContextProviderWithValues />
|
|
|
|
Et pour terminer, on va maintenant pouvoir directement imbriquer notre `ThemeProvider` dans notre `App` :
|
|
|
|
<tabs.reactContextProviderInApp />
|
|
|
|
## Utilisation d'un contexte
|
|
|
|
C'est bien beau de créer un contexte, mais comment l'utiliser ?
|
|
Tu te souviens peut-être du `Consumer` que l'on a évoqué plus tôt, non ?
|
|
|
|
Et bien, il est temps de le mettre en pratique ! 😁
|
|
|
|
Pour commencer, nous allons avoir besoin du hook `useContext` de React.
|
|
Ce hook va nous permettre de récupérer les données du contexte, et ce, directement dans nos composants.
|
|
|
|
<tabs.reactUseContext />
|
|
|
|
Pas mal, non ? 😉
|
|
Fini l'arborescence de composants à rallonge, on peut maintenant récupérer les données du contexte directement dans nos composants !
|
|
|
|
## Les défauts des contextes
|
|
|
|
Seulement... Un grand pouvoir implique de grandes responsabilités. 🕷️
|
|
|
|
Bien que les contextes soient très pratiques, il faut prendre en compte quelques points :
|
|
|
|
- On ne peut pas utiliser les contextes pour tout et n'importe quoi. Ils sont plutôt adaptés pour diffuser des données qui sont utilisées par plusieurs composants.
|
|
- Les contextes peuvent rendre le code plus difficile à comprendre.
|
|
- L'utilisation de nombreux contextes va faire apparaître ce qu'on appelle le **context hell**.
|
|
|
|
### Le context hell
|
|
|
|
Dans cet article, nous avons vu comment créer un contexte et l'utiliser.
|
|
Et par chance, nous n'avons pas encore rencontré le **context hell**.
|
|
|
|
Mais maintenant, que se passe-t-il si on a besoin de plusieurs contextes _(plusieurs dizaines par exemple !)_ dans notre application ?
|
|
On va se retrouver avec une arborescence de composants qui va devenir de plus en plus difficile à comprendre et à maintenir.
|
|
|
|
Et c'est ça, le **context hell**.
|
|
|
|
<tabs.reactContextHell />
|
|
|
|
Maintenant, demande à un développeur d'inverser le provider `UserProvider` avec le provider `NoteProvider`.
|
|
C'est jouable sans difficulté, mais si tu entends des cris de désespoir, c'est normal. 😅
|
|
|
|
Pour éviter de tomber dans le **context hell**, il est important de bien réfléchir à l'utilisation des contextes dans notre application avec ces quelques questions :
|
|
|
|
- Est-ce que l'utilisation d'un contexte est vraiment nécessaire pour ce cas d'usage ?
|
|
- Est-ce que le contexte est utilisé par plusieurs composants ?
|
|
- Est-ce que le contexte est utilisé par des composants éloignés dans l'arborescence ?
|
|
|
|
Mais alors, si tu as besoin d'autant de contextes dans ton application, comment faire ?
|
|
Et bien, il existe des solutions pour éviter le **context hell** :
|
|
|
|
- Utiliser des bibliothèques tierces comme Redux _(solution lourde, mais très puissante)_
|
|
- Créer un nouveau composant qui va regrouper tous les contextes _(solution plus légère, mais plus difficile à maintenir)_
|
|
|
|
N'étant pas un grand fan de Redux, je vais plutôt te présenter la deuxième solution.
|
|
Mais si tu veux en savoir plus sur Redux, n'hésite pas à consulter la documentation officielle !
|
|
|
|
### Résoudre le context hell avec un composant dédié
|
|
|
|
Parlons de ce fameux composant qui va regrouper tous les contextes !
|
|
On ne parle pas ici d'un simple composant Providers qui va imbriquer tous les Provider de nos contextes, mais d'une solution plus élégante.
|
|
|
|
Après tout, nous sommes des feignants développeurs, non ? 😏
|
|
|
|
Réfléchissons à ce que l'on veut faire :
|
|
|
|
- On veut pouvoir regrouper tous les contextes dans un seul composant.
|
|
- On veut pouvoir ajouter ou supprimer des contextes facilement.
|
|
- On veut pouvoir facilement les ordonner entre eux.
|
|
- On veut éviter le **context hell**.
|
|
|
|
Et si on créait un composant Providers qui va nous permettre de faire tout ça ?
|
|
|
|
<tabs.reactContextProvidersComponent />
|
|
|
|
Ici on ne va pas remettre une cascade de Provider comme on a pu le voir plus tôt.
|
|
On va chercher à créer une fonction qui va nous permettre de les imbriquer les uns dans les autres.
|
|
|
|
<tabs.reactNestFunction />
|
|
|
|
<Callout type="note" title="React.cloneElement">
|
|
`React.cloneElement` est une fonction qui va permettre de cloner un élément React en lui passant de nouvelles propriétés.
|
|
Cela va nous permettre de créer une nouvelle arborescence de composants sans modifier l'arborescence actuelle.
|
|
|
|
Le premier argument est l'élément à cloner _(le composant)_, et le deuxième argument est un objet contenant les nouvelles propriétés.
|
|
Le troisième argument est le contenu de l'élément cloné _(les enfants)_.
|
|
</Callout>
|
|
|
|
Et maintenant, on va pouvoir utiliser notre fonction `nest` pour imbriquer nos Provider en utilisant la méthode `reduceRight` :
|
|
|
|
<tabs.reactNestFunctionWithReduceRight />
|
|
|
|
<Callout type="note" title="reduceRight">
|
|
`reduceRight` est une méthode qui va permettre de réduire un tableau _(ou un objet)_ en appliquant une fonction de rappel de droite à gauche.
|
|
Cela va nous permettre de réduire un tableau de `Provider` en les imbriquant les uns dans les autres sans se soucier de l'ordre _(qui est défini par le tableau)_.
|
|
|
|
Dans l'idée, on commence par le **dernier** élément du tableau, et on l'imbrique avec l'élément **précédent** du tableau et ainsi de suite jusqu'au **premier** élément du tableau.
|
|
Chaque itération va créer un nouvel élément imbriqué dans le précédent, en appelant la fonction `nest` qui est passée en argument.
|
|
</Callout>
|
|
|
|
Et voilà ! Il ne nous reste plus qu'à utiliser notre composant `Providers` pour regrouper tous nos `Provider` :
|
|
|
|
<tabs.reactCleanerProviders />
|
|
|
|
Évidemment le fichier contiendra toujours beaucoup de lignes, mais au moins, on a évité le **context hell** !
|
|
Il sera nettement plus facile de modifier l'ordre des Provider ou d'en ajouter de nouveaux.
|
|
|
|
## Conclusion
|
|
|
|
Ça casse un peu la tête, mais les contextes sont un outil très puissant pour diffuser des données dans nos applications React.
|
|
|
|
C'est aussi une excellente solution pour éviter d'utiliser des bibliothèques tierces comme Redux _(qui est très bien, mais qui peut être un peu lourd pour des petites applications)_.
|
|
On prendra d'ailleurs le temps de parler de Redux et de Zustand dans un prochain article 😉
|
|
|
|
Et si tu as besoin de plusieurs contextes dans ton application, n'oublie pas de réfléchir à l'utilisation de notre composant Providers pour éviter le **context hell**.
|