Comprendre React Context en faisant un Dark mode
Tags: react, javascript, beginners, tutorial
Published: 2023-04-01 13:58:48

Dans ce tutoriel nous allons voir ensemble ce qu'est React Context et finir cette explication par un petit *dark mode* pour la mise en pratique.

Déjà, c'est quoi React Context ?

React Context (ou l'API de Context pour les plus intimes) va nous permettre de rendre disponible des données et/ou des fonctions, dans toute notre application.

Sans React Context, il est possible de le faire en passant par les props d'un parent à son enfant. Cependant, imaginez que vous ayez une architecture où vous avez un parent, qui à un enfant, qui à lui même un enfant, qui à lui même un enfant, etc.
Il serait fastidieux de mettre cela en place.

![prop drilling image](https://up-blog-prod.s3.amazonaws.com/FC1-1180x664-30poq.jpg)
On appelle ça le **prop drilling**. 

Context existe donc pour éviter le prop drilling.
Pour faire une métaphore, imaginez une famille dans une maison où l'on a le père, le fils, le petit-fils et l'arrière petit-fils.

Avec le prop drilling :
Le père souhaite donner les clés de la voiture familial à son arrière petit-fils. Pour cela, il doit donner les clés à son fils, qui va devoir les donner à son petit-fils, qui les donnera enfin à son arrière petit-fils.

Avec Context :
Le père pose les clés de la voiture dans le salon et celui qui à besoin des clés n'aura qu'a les récupérer. Les clés sont disponible pour tout le monde, sans passer par un intermédiaire.
Mais n'oublions pas les bonnes manières… Il faudra les demander, avant de les prendre :wink:

![prop drilling vs context api](https://miro.medium.com/v2/resize:fit:720/format:webp/1*mOpxRXP7O0laXUDIqzm2_Q.png)

Maintenant que nous avons vu la théorie, faisons un peu de pratique avec notre dark mode.
![gif jour nuit les visiteurs](https://thumbs.gfycat.com/UnhappyRedHoverfly-max-1mb.gif)
Il y aura 3 étapes principales :
- Créer le *context*
- Englober notre app dans le *context* créé
- Récupérer les données/fonctions de notre *context*

Juste pour le côté pratique, je vais tout mettre dans le dossier `src`, et configurer le css dans un seul endroit. Ne gardez que les fichiers suivants : `index.js` | `index.css` | `App.js`

```css
/* index.css */

*, ::before, ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
.app {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  width: 100vw;
  height: 100vh;
}
.dark-theme {
  background-color: #1B2430;
  color: #F1F1F1;
}
.content {
  width: 80%;
}
```

Créons un peu de contenu pour nous permettre de voir le changement qui va s'opérer avec le dark mode ainsi que le bouton qui vas nous permettre de switcher avec ce mode.

```react
// ToggleButton.js
export default function ToggleButton() {
  return <button>Toggle</button>
}
```
```react
// Content.js
import ToggleButton from "./ToggleButton";

export default function Content() {
  return (
    <div className="content">
      <h1>Hello world!</h1>
      <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. 
         Voluptates id ratione voluptatibus iusto unde soluta 
         dicta cum nam, rerum sit labore at et ducimus nobis 
         expedita molestiae commodi nostrum eius voluptatem 
         reiciendis repellat accusamus placeat eveniet rem. 
         Consequuntur, ducimus dolorum. Sunt deserunt optio itaque 
         maxime eligendi temporibus magnam pariatur inventore.</p>
         <ToggleButton />
    </div>
  )
}
```
```react
// App.js
import Content from "./Content";

export default function App() {
  return (
    <div className="app">
      <Content />
    </div>
  )
}
```

Maintenant que nous avons notre base, passons à ce qui nous intéresse vraiment et créons le fichier qui va fournir notre *context* : `DarkModeProvider.js`.

## 1. Créer le context
Pour créer un *context*, rien de plus simple, il suffit d'utiliser la commande suivante :
`export const DarkModeContext = createContext();`

:red_circle: Attention ! Cette commande doit se trouver en dehors de vos composants. Notre fichier ressemble maintenant à cela pour le moment :

```react
// DarkModeProvider.js
import { createContext } from "react"

export const DarkModeContext = createContext();

// début du composant
export default function DarkModeProvider() {
  return (
    <div>
      
    </div>
  )
}
// fin du composant

```

Pour la suite, nous allons créer une variable `darkMode` qui aura pour valeur `true` ou `false` afin de savoir si le dark mode est activé. À cela, nous allons ajouter une fonction qui nous permettra permettra de switcher entre ces deux valeurs.

Ça tombe bien, il y a un Hook que nous allons utiliser pour cette tâche et c'est `useState`.

```react
import { createContext, useState } from "react"

export const DarkModeContext = createContext();

export default function DarkModeProvider() {
  const [darkMode, setDarkMode] = useState(false);

  function toggleDarkMode() {
    setDarkMode(!darkMode);
  }

  return (
    <div>
      
    </div>
  )
}
```
On initialise darkMode à false car initialement notre site sera en clair (couleur par défaut) : 
false -> clair
true -> sombre

Puis la fonction `toggleDarkMode()` permettra à chaque clique sur notre bouton, d'inverser les couleurs, c'est à dire de faire passer `false` à sa valeur inverse : `true` et ainsi de suite.

D'ailleurs, comment je fais pour transmettre cette fonction à mon bouton `ToggleButton` ?

## 2. Englober notre app dans le context créé
Il y a deux étapes pour "englober" notre app de notre context. On peut dire aussi que l'on fournis un context à notre app.

La première est d'englober notre composant `App` par le composant `DarkModeProvider` :

```react
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import DarkModeProvider from "./DarkModeProvider";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <DarkModeProvider>
      <App />
    </DarkModeProvider>
  </React.StrictMode>
);
```
```react
// DarkModeProvider.js
import { createContext, useState } from "react"

export const DarkModeContext = createContext();

export default function DarkModeProvider({ children }) {
  const [darkMode, setDarkMode] = useState(false);

  function toggleDarkMode() {
    setDarkMode(!darkMode);
  }

  return (
    <DarkModeContext.Provider>
      {children}
    </DarkModeContext.Provider>
  )
}
```
Ici, `children` correspond à notre composant `App`.


Autre petite étape avant de passer à la suite. On a rendu notre context disponible, c'est bien... Mais comment Context sait qu'il doit mettre telle ou telle donnée à disposition ?

Il faut les ajouter en tant que valeur comme ceci :
```react
// DarkModeProvider.js
  return (
    <DarkModeContext.Provider 
    value={ {darkMode, toggleDarkMode} }>
      {children}
    </DarkModeContext.Provider>
  )
}
```
On ajoute donc un objet avec ce que l'on souhaite rendre disponible.

## 3. Récupérer les données/fonctions de notre *context*
Est-ce que vous vous rappelez de ce que je vous ai dit plus haut ? Lorsque l'on souhaite obtenir quelque chose, on le demande gentiment. Ça fait partie des bonnes manières.

Pour obtenir ce que l'on souhaite, nous allons utiliser le Hook **useContext**. Il va nous permettre d'utiliser ce que Context à rendu disponible.

Avec cette ligne et grâce au *destructuring*, nous nous permettons de récupérer notre donnée et notre fonction renduent disponible par notre Context :
`const {darkMode, toggleDarkMode} = useContext(DarkModeContext);`

Maintenant que nous avons accès à notre fonction, nous allons l'ajouter et l'exécuter lorsque le bouton sera cliqué et pour vous montrer que cela fonctionne, nous allons loguer la valeur de `darkMode`.

Votre code devrait ressembler à ceci : 
```react
import { useContext } from "react"
import { DarkModeContext } from "./DarkModeProvider"

export default function ToggleButton() {
  const {darkMode, toggleDarkMode} = useContext(DarkModeContext);
  console.log(darkMode);
  return <button onClick={toggleDarkMode}>Toggle</button>
}
```
Maintenant, ouvrez la console et cliquez sur sur le bouton et vous verrez la valeur de `darkMode` passer de `false` à `true` et ainsi de suite.

Super ! On a terminé toutes les étapes pour apprendre à utiliser les propriétés transmises par Context.

De ce fait, voici le code qui nous permet d'ajouter la classe *dark-theme* pour notre app :

```react
import { useContext } from "react";
import Content from "./Content";
import { DarkModeContext } from "./DarkModeProvider";

export default function App() {
  const {darkMode} = useContext(DarkModeContext);
  return (
    <div className={`app ${darkMode && 'dark-theme'}`}>
        <Content />
    </div>
  )
}
```
On récupère la valeur de `darkMode` et si elle est `true`, alors la classe sera ajouté.

## Conclusion
*Context* nous permets de rendre disponible nos données/fonctions beaucoup plus facilement. On utilise *Context* généralement pour des actions occasionnels (changement de thème, changement de la langue du site, …).


Je vous invite maintenant à lire la documentation officielle de React sur *Context* et *useContext* pour parfaire vos connaissances et réaliser les exemples qu'ils proposent.

Sayonara !