feat: Add React useContext documentation page
This commit is contained in:
parent
15cc58f07f
commit
8ad370940f
168
app/pages/docs/react/use-context/+Page.mdx
Normal file
168
app/pages/docs/react/use-context/+Page.mdx
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
---
|
||||||
|
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**.
|
||||||
@ -1,537 +0,0 @@
|
|||||||
---
|
|
||||||
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)]
|
|
||||||
---
|
|
||||||
|
|
||||||
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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [theme, setTheme] = useState("light");
|
|
||||||
|
|
||||||
return <A theme={theme} setTheme={theme} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const A = ({ theme, setTheme }) => {
|
|
||||||
return <B theme={theme} setTheme={setTheme} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const B = ({ theme, setTheme }) => {
|
|
||||||
return <C theme={theme} setTheme={setTheme} />;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
type Theme = "light" | "dark";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [theme, setTheme] = useState<Theme>("light");
|
|
||||||
|
|
||||||
return <A theme={theme} setTheme={theme} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const A = ({ theme, setTheme }: { theme: Theme; setTheme: Dispatch<SetStateAction<Theme>> }) => {
|
|
||||||
return <B theme={theme} setTheme={setTheme} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const B = ({ theme, setTheme }: { theme: Theme; setTheme: Dispatch<SetStateAction<Theme>> }) => {
|
|
||||||
return <C theme={theme} setTheme={setTheme} />;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { createContext } from "react";
|
|
||||||
|
|
||||||
// On crée notre contexte, avec une valeur par défaut : un thème clair
|
|
||||||
const ThemeContext = createContext({
|
|
||||||
theme: "light",
|
|
||||||
setTheme: () => {},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
|
||||||
|
|
||||||
import { createContext } from "react";
|
|
||||||
|
|
||||||
// On crée un type pour les valeurs de thème
|
|
||||||
export type Theme = "light" | "dark";
|
|
||||||
|
|
||||||
// On crée un type pour notre contexte
|
|
||||||
type ThemeContextType = {
|
|
||||||
theme: Theme;
|
|
||||||
setTheme: Dispatch<SetStateAction<Theme>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// On crée notre contexte, avec une valeur par défaut : un thème clair
|
|
||||||
const ThemeContext = createContext<ThemeContextType>({
|
|
||||||
theme: "light",
|
|
||||||
setTheme: () => {},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
## 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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [theme, setTheme] = useState("light");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeContext.Provider value={{ theme, setTheme }}>
|
|
||||||
<A />
|
|
||||||
</ThemeContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import type { Theme } from "./ThemeContext";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [theme, setTheme] = useState<Theme>("light");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeContext.Provider value={{ theme, setTheme }}>
|
|
||||||
<A />
|
|
||||||
</ThemeContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { createContext, useState } from "react";
|
|
||||||
|
|
||||||
const ThemeContext = createContext({
|
|
||||||
theme: "light",
|
|
||||||
setTheme: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
const ThemeProvider = ({ children }) => {
|
|
||||||
const [theme, setTheme] = useState("light");
|
|
||||||
|
|
||||||
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { ThemeContext, ThemeProvider };
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import type { ReactNode } from "react";
|
|
||||||
|
|
||||||
import { createContext, useState } from "react";
|
|
||||||
|
|
||||||
export type Theme = "light" | "dark";
|
|
||||||
|
|
||||||
type ThemeContextType = {
|
|
||||||
theme: Theme;
|
|
||||||
setTheme: Dispatch<SetStateAction<Theme>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ThemeContext = createContext<ThemeContextType>({
|
|
||||||
theme: "light",
|
|
||||||
setTheme: () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
type ThemeProviderProps = {
|
|
||||||
children: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ThemeProvider = ({ children }: ThemeProviderProps) => {
|
|
||||||
const [theme, setTheme] = useState<Theme>("light");
|
|
||||||
|
|
||||||
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { ThemeContext, ThemeProvider };
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
Et pour terminer, on va maintenant pouvoir directement imbriquer notre `ThemeProvider` dans notre `App` :
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { ThemeProvider } from "./ThemeContext";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
return (
|
|
||||||
<ThemeProvider>
|
|
||||||
<A />
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
import { ThemeContext } from "./ThemeContext";
|
|
||||||
import { useContext } from "react";
|
|
||||||
|
|
||||||
const C = () => {
|
|
||||||
const { theme, setTheme } = useContext(ThemeContext);
|
|
||||||
|
|
||||||
return <>{/** JSX */}</>;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
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**.
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
root.render(
|
|
||||||
<StrictMode>
|
|
||||||
<UserProvider>
|
|
||||||
<ThemeProvider>
|
|
||||||
<LanguageProvider>
|
|
||||||
<PostProvider>
|
|
||||||
<SettingsProvider>
|
|
||||||
<SocketProvider>
|
|
||||||
<FriendProvider>
|
|
||||||
<NotificationProvider>
|
|
||||||
<ChatProvider>
|
|
||||||
<MusicProvider>
|
|
||||||
<VideoProvider>
|
|
||||||
<GameProvider>
|
|
||||||
<WeatherProvider>
|
|
||||||
<NewsProvider>
|
|
||||||
<CalendarProvider>
|
|
||||||
<TaskProvider>
|
|
||||||
<NoteProvider>
|
|
||||||
<App />
|
|
||||||
</NoteProvider>
|
|
||||||
</TaskProvider>
|
|
||||||
</CalendarProvider>
|
|
||||||
</NewsProvider>
|
|
||||||
</WeatherProvider>
|
|
||||||
</GameProvider>
|
|
||||||
</VideoProvider>
|
|
||||||
</MusicProvider>
|
|
||||||
</ChatProvider>
|
|
||||||
</NotificationProvider>
|
|
||||||
</FriendProvider>
|
|
||||||
</SocketProvider>
|
|
||||||
</SettingsProvider>
|
|
||||||
</PostProvider>
|
|
||||||
</LanguageProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</UserProvider>
|
|
||||||
</StrictMode>,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
const Providers = ({ providers, children }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/** Ouverture des providers */}
|
|
||||||
{children}
|
|
||||||
{/** Fermeture des providers */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import type { ReactNode } from "react";
|
|
||||||
|
|
||||||
type ProvidersProps = {
|
|
||||||
providers: ReactNode[];
|
|
||||||
children: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Providers = ({ providers, children }: ProvidersProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/** Ouverture des providers */}
|
|
||||||
{children}
|
|
||||||
{/** Fermeture des providers */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
const nest = (children, component) => {
|
|
||||||
return React.cloneElement(component, {}, children);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
const nest = (children: ReactNode, component: ReactNode) => {
|
|
||||||
return React.cloneElement(component, {}, children);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
{% 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 defaultSelectedTab="jsx" %}
|
|
||||||
|
|
||||||
{% tab value="jsx" label="JSX" %}
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
const nest = (children, component) => {
|
|
||||||
return React.cloneElement(component, {}, children);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Providers = ({ providers, children }) => {
|
|
||||||
return providers.reduceRight(nest, children);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% tab value="tsx" label="TSX" %}
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import type { ReactNode } from "react";
|
|
||||||
|
|
||||||
type ProvidersProps = {
|
|
||||||
providers: ReactNode[];
|
|
||||||
children: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const nest = (children: ReactNode, component: ReactNode) => {
|
|
||||||
return React.cloneElement(component, {}, children);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Providers = ({ providers, children }: ProvidersProps) => {
|
|
||||||
return providers.reduceRight(nest, children);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
{% /tab %}
|
|
||||||
|
|
||||||
{% /tabs %}
|
|
||||||
|
|
||||||
{% 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` :
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
root.render(
|
|
||||||
<StrictMode>
|
|
||||||
<Providers
|
|
||||||
providers={[
|
|
||||||
<UserProvider />,
|
|
||||||
<ThemeProvider />,
|
|
||||||
<LanguageProvider />,
|
|
||||||
<PostProvider />,
|
|
||||||
<SettingsProvider />,
|
|
||||||
<SocketProvider />,
|
|
||||||
<FriendProvider />,
|
|
||||||
// ...
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<App />
|
|
||||||
</Providers>
|
|
||||||
</StrictMode>,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
É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**.
|
|
||||||
397
app/pages/docs/react/use-context/tabs.tsx
Normal file
397
app/pages/docs/react/use-context/tabs.tsx
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
import { Snippet } from "@/components/Snippet";
|
||||||
|
import { name } from "eslint-plugin-prettier/recommended";
|
||||||
|
|
||||||
|
const reactNestedPropsSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import { useState } from "react";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [theme, setTheme] = useState("light");
|
||||||
|
|
||||||
|
return <A theme={theme} setTheme={theme} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const A = ({ theme, setTheme }) => {
|
||||||
|
return <B theme={theme} setTheme={setTheme} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const B = ({ theme, setTheme }) => {
|
||||||
|
return <C theme={theme} setTheme={setTheme} />;
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import type { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
type Theme = "light" | "dark";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [theme, setTheme] = useState<Theme>("light");
|
||||||
|
|
||||||
|
return <A theme={theme} setTheme={theme} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const A = ({ theme, setTheme }: { theme: Theme; setTheme: Dispatch<SetStateAction<Theme>> }) => {
|
||||||
|
return <B theme={theme} setTheme={setTheme} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const B = ({ theme, setTheme }: { theme: Theme; setTheme: Dispatch<SetStateAction<Theme>> }) => {
|
||||||
|
return <C theme={theme} setTheme={setTheme} />;
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactCreateContextSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import { createContext } from "react";
|
||||||
|
|
||||||
|
// On crée notre contexte, avec une valeur par défaut : un thème clair
|
||||||
|
const ThemeContext = createContext({
|
||||||
|
theme: "light",
|
||||||
|
setTheme: () => {},
|
||||||
|
});`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import type { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
// On crée un type pour les valeurs de thème
|
||||||
|
export type Theme = "light" | "dark";
|
||||||
|
|
||||||
|
// On crée un type pour notre contexte
|
||||||
|
type ThemeContextType = {
|
||||||
|
theme: Theme;
|
||||||
|
setTheme: Dispatch<SetStateAction<Theme>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// On crée notre contexte, avec une valeur par défaut : un thème clair
|
||||||
|
const ThemeContext = createContext<ThemeContextType>({
|
||||||
|
theme: "light",
|
||||||
|
setTheme: () => {},
|
||||||
|
});`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactContextProviderSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import { useState } from "react";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [theme, setTheme] = useState("light");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={{ theme, setTheme }}>
|
||||||
|
<A />
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import type { Theme } from "./ThemeContext";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [theme, setTheme] = useState<Theme>("light");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={{ theme, setTheme }}>
|
||||||
|
<A />
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactContextProviderWithValuesSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import { createContext, useState } from "react";
|
||||||
|
|
||||||
|
const ThemeContext = createContext({
|
||||||
|
theme: "light",
|
||||||
|
setTheme: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThemeProvider = ({ children }) => {
|
||||||
|
const [theme, setTheme] = useState("light");
|
||||||
|
|
||||||
|
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ThemeContext, ThemeProvider };`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
import { createContext, useState } from "react";
|
||||||
|
|
||||||
|
export type Theme = "light" | "dark";
|
||||||
|
|
||||||
|
type ThemeContextType = {
|
||||||
|
theme: Theme;
|
||||||
|
setTheme: Dispatch<SetStateAction<Theme>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ThemeContext = createContext<ThemeContextType>({
|
||||||
|
theme: "light",
|
||||||
|
setTheme: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
type ThemeProviderProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ThemeProvider = ({ children }: ThemeProviderProps) => {
|
||||||
|
const [theme, setTheme] = useState<Theme>("light");
|
||||||
|
|
||||||
|
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ThemeContext, ThemeProvider };`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactContextProviderInAppSnippets = [
|
||||||
|
{
|
||||||
|
name: "App.jsx",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import { ThemeProvider } from "./ThemeContext";
|
||||||
|
import A from "./A";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
|
<A />
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactUseContextSnippets = [
|
||||||
|
{
|
||||||
|
name: "Utilisation du hook `useContext`",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import { ThemeContext } from "./ThemeContext";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
const C = () => {
|
||||||
|
const { theme, setTheme } = useContext(ThemeContext);
|
||||||
|
|
||||||
|
return <>{/** JSX */}</>;
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactContextHellSnippets = [
|
||||||
|
{
|
||||||
|
name: "Exemple de contexte imbriqué",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<UserProvider>
|
||||||
|
<ThemeProvider>
|
||||||
|
<LanguageProvider>
|
||||||
|
<PostProvider>
|
||||||
|
<SettingsProvider>
|
||||||
|
<SocketProvider>
|
||||||
|
<FriendProvider>
|
||||||
|
<NotificationProvider>
|
||||||
|
<ChatProvider>
|
||||||
|
<MusicProvider>
|
||||||
|
<VideoProvider>
|
||||||
|
<GameProvider>
|
||||||
|
<WeatherProvider>
|
||||||
|
<NewsProvider>
|
||||||
|
<CalendarProvider>
|
||||||
|
<TaskProvider>
|
||||||
|
<NoteProvider>
|
||||||
|
<App />
|
||||||
|
</NoteProvider>
|
||||||
|
</TaskProvider>
|
||||||
|
</CalendarProvider>
|
||||||
|
</NewsProvider>
|
||||||
|
</WeatherProvider>
|
||||||
|
</GameProvider>
|
||||||
|
</VideoProvider>
|
||||||
|
</MusicProvider>
|
||||||
|
</ChatProvider>
|
||||||
|
</NotificationProvider>
|
||||||
|
</FriendProvider>
|
||||||
|
</SocketProvider>
|
||||||
|
</SettingsProvider>
|
||||||
|
</PostProvider>
|
||||||
|
</LanguageProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</UserProvider>
|
||||||
|
</StrictMode>,
|
||||||
|
);`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactContextProvidersComponentSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `const Providers = ({ providers, children }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/** Ouverture des providers */}
|
||||||
|
{children}
|
||||||
|
{/** Fermeture des providers */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
withLineNumbers: true,
|
||||||
|
code: `import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
type ProvidersProps = {
|
||||||
|
providers: ReactNode[];
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Providers = ({ providers, children }: ProvidersProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/** Ouverture des providers */}
|
||||||
|
{children}
|
||||||
|
{/** Fermeture des providers */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactNestFunctionSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
code: `const nest = (children, component) => {
|
||||||
|
return React.cloneElement(component, {}, children);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
code: `const nest = (children: ReactNode, component: ReactNode) => {
|
||||||
|
return React.cloneElement(component, {}, children);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactNestFunctionWithReduceRightSnippets = [
|
||||||
|
{
|
||||||
|
name: "JSX",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
code: `const nest = (children, component) => {
|
||||||
|
return React.cloneElement(component, {}, children);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Providers = ({ providers, children }) => {
|
||||||
|
return providers.reduceRight(nest, children);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TSX",
|
||||||
|
codeLanguage: "tsx",
|
||||||
|
code: `import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
type ProvidersProps = {
|
||||||
|
providers: ReactNode[];
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nest = (children: ReactNode, component: ReactNode) => {
|
||||||
|
return React.cloneElement(component, {}, children);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Providers = ({ providers, children }: ProvidersProps) => {
|
||||||
|
return providers.reduceRight(nest, children);
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactCleanerProvidersSnippets = [
|
||||||
|
{
|
||||||
|
name: "Import lisible et maintenable pour les providers",
|
||||||
|
codeLanguage: "jsx",
|
||||||
|
code: `root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<Providers
|
||||||
|
providers={[
|
||||||
|
<UserProvider />,
|
||||||
|
<ThemeProvider />,
|
||||||
|
<LanguageProvider />,
|
||||||
|
<PostProvider />,
|
||||||
|
<SettingsProvider />,
|
||||||
|
<SocketProvider />,
|
||||||
|
<FriendProvider />,
|
||||||
|
// ...
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<App />
|
||||||
|
</Providers>
|
||||||
|
</StrictMode>,
|
||||||
|
);`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
reactNestedProps: () => <Snippet snippets={reactNestedPropsSnippets} />,
|
||||||
|
reactCreateContext: () => <Snippet snippets={reactCreateContextSnippets} />,
|
||||||
|
reactContextProvider: () => (
|
||||||
|
<Snippet snippets={reactContextProviderSnippets} />
|
||||||
|
),
|
||||||
|
reactContextProviderWithValues: () => (
|
||||||
|
<Snippet snippets={reactContextProviderWithValuesSnippets} />
|
||||||
|
),
|
||||||
|
reactContextProviderInApp: () => (
|
||||||
|
<Snippet snippets={reactContextProviderInAppSnippets} />
|
||||||
|
),
|
||||||
|
reactUseContext: () => <Snippet snippets={reactUseContextSnippets} />,
|
||||||
|
reactContextHell: () => <Snippet snippets={reactContextHellSnippets} />,
|
||||||
|
reactContextProvidersComponent: () => (
|
||||||
|
<Snippet snippets={reactContextProvidersComponentSnippets} />
|
||||||
|
),
|
||||||
|
reactNestFunction: () => <Snippet snippets={reactNestFunctionSnippets} />,
|
||||||
|
reactNestFunctionWithReduceRight: () => (
|
||||||
|
<Snippet snippets={reactNestFunctionWithReduceRightSnippets} />
|
||||||
|
),
|
||||||
|
reactCleanerProviders: () => (
|
||||||
|
<Snippet snippets={reactCleanerProvidersSnippets} />
|
||||||
|
),
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user