Le UseContext

Le UseContext

La version 16.8 de react a été une petite révolution chez ceux utilisant déjà le langage. En plus de revoir les fondamentaux et de se séparer de certaines choses, la mise à jour a permis de nombreuses améliorations, dont le useContext. Si vous êtes du genre à encapsuler des composants dans des composants et de faire passer des données en escalier, useContext fera votre bonheur.

Ryan Djoher

15/05/2021 - 5 minutes de lecture

Avant de vous expliquer comment utiliser les useContext, voyons un peu la chose. Cette petite méthode, très simple à utiliser, vous permet d’avoir une espèce de « mémoire » interne à votre appli et distribue ces données à qui veut. Vous verrez souvent le terme de « global state ». Oui, c’est un peu comme Redux mais en moins lourd / complexe. Attention, loin de moi l’envie de vous dire que useContext est mieux que Redux ! Disons que UseContext est bien pour des partages de données pas très complexes quand Redux est plus adapté pour des projets avec un global state qui évoluera sans cesse. Bref, ne partez pas toujours du fait que useContext est mieux.

Revenons à nos petits moutons. Imaginons que nous avons plusieurs composants et que celui qui est le plus en haut (App) veut passer des données à celui qui est le plus bas (Compo3). Voila comment on ferait sans le useContext.

App > Compo1 > Compo2 > Compo3

import React, { useState } from 'react';
import Compo1 from "./Components/Compo1"

function App() {

  const [profile, setProfile] = useState({name:"Ryan"})

  return (
    <Compo1 profile={profile}/>
  );
}

export default App;
import React from "react"
import Compo2 from "./Compo2"

export default function Compo1({profile}){

  return(
    <Compo2 profile={profile} />
  )
}
import React from "react"
import Compo3 from "./Compo3"

export default function Compo2({profile}){

  return(
    <Compo3 profile={profile} />
  )
}
import React from "react"

export default function Compo3({profile}){

  return(
  <div>
    {profile.name}
  </div>
  )
}

Le composant App a une info qu’il veut faire passer à Compo3, mais il est plusieurs niveaux en dessous, il doit donc le faire passer à tous les composants qui sont entre eux. Non seulement c’est redondant à faire, mais ça peut devenir assez lourd si on veut faire une modification (et oui, il faudra les faire partout). C’est là que le useContext ente en jeu.

Cette méthode, née avec les hooks, va nous permettre de passer de la data du composant App au composant Compo3 directement, sans devoir écrire sur les composants qui sont au milieu. Voyons maintenant comment ça se passe.

En premier lieu, il va falloir créer la donnée et surtout, le fichier qui le contiendra. Personnellement, je recommande de créer à la racine du projet (ou dans le dossier src comme moi) un dossier qui contiendrai tous les useContext. Voilà à quoi il ressemblera :

import React, { createContext } from 'react'

const videoGame = createContext()

export default videoGame

Pour ceux qui aiment la clarté, voici comment se présente dorénavant le projet. Pour info, j’ai utilisé le CRA (ceate-react-app) pour avoir rapidement un environnement prêt.

image pour habiller article

Notre variable videoGame est vide, nous avons juste stipulé qu’elle sera un useContext. Et bien sûr, nous l’avons exporté afin qu’elle soit disponible partout dans le projet. Maintenant nous allons l’appelé dans la composant App.

import VideoGame from './useContext/videoGame'

L’invoqué c’est bien, l’utilisé c’est mieux. Et c’est là que ça se corse petit peu. Nous avons besoin d’abonné le composant à toutes les futures mises à jour de notre variable videoGame. Nous avons donc besoin d’un provider fourni par React. Voici le composant App au complet.

import React, { useState } from 'react';
import VideoGame from './useContext/videoGame'
import Compo1 from "./Components/Compo1"


function App() {

  const [profile, setProfile] = useState({name:"Ryan"})

  return (
    <VideoGame.Provider>
      <Compo1/>
    </VideoGame.Provider>
  );
}

export default App;

Vous avez dû le remarquer, mais j’ai retiré le paramètre « profile » du composant Compo1. Je l’ai aussi fait sur les composants suivants vu qu’on va passer par la méthode useContext cette fois.

Souvenez-vous, la variable videoGame est vide, il va donc falloir lui passer une valeur et c’est dans les paramètres du composant Provider que ça va se passer.

    <VideoGame.Provider value={"persona 5"}>
      <Compo1/>
    </VideoGame.Provider>

A partir de maintenant, la valeur qui sera passée au composant sera le nom du jeu. Faisons un peu le point : En premier lieu nous avons une variable vide qui est un useContexte, en second lieu nous avons un composant abonné aux différentes mises à jour de cette variable et enfin, nous donnons à la variable videoGame une valeur.

Ce qui manque, c’est de récupérer les données de videoGame dans le compo3 sans passer par les autres. Et là, vous allez voir que c’est très simple.

import React, {useContext} from 'react'
import videoGame from "../useContext/videoGame"

export default function Compo3(){

  const gameTitle = useContext(videoGame);

  return(
  <div>
    {gameTitle}
  </div>
  )
}

Et voilà le travail ! Il nous suffit simplement d’importer le useContext videoGame et de préciser dans la fonction que c’est un useContext. Nous avons fait ce que nous voulions.

Modifier le useContext

On va passer au level au-dessus : Imaginons que Compo3 veut modifier le titre du jeu. Il va donc faire une modification de la variable et comme un utilise un provider pour écouter toutes les modifications, alors la mise à jour se fera automatiquement. J’ai fait quelques modifications sur le composant App mais rien de bien méchant.

import React, { useState } from 'react';
import VideoGame from './useContext/videoGame'
import Compo1 from "./Components/Compo1"


function App() {

  const [gameTitle, setGameTitle] = useState({name:"Persona 5"})

  return (
    <VideoGame.Provider value={{gameTitle, setGameTitle}}>
      <Compo1/>
    </VideoGame.Provider>
  );
}

export default App;

Le principal est que dans le paramètre « value » du composant provider VideoGame, on envoie un objet contenant la variable gameTitle (et non plus profile, j’ai changé) et sa fonction afin de ma modifier. Si vous êtes un minimum à l’aise avec les hooks, normalement il ne devrait pas y avoir de compréhension. Maintenant allons voir le composant Compo3.

import React, {useContext} from 'react'
import videoGame from "../useContext/videoGame"

export default function Compo3(){

  const { gameTitle, setGameTitle } = useContext(videoGame)

  return(
  <div>
    {gameTitle.name}
    <button onClick={e=>setGameTitle({name:"Mario"})}>Changer le nom du jeu</button>
  </div>
  )
}

Ici, on récupère la variable « gameTitle » et sa fonction modificatrice « setGameTitle » et on les utilise dans le render. Si on clique sur le bouton, vous allez voir que le gameTitle va changer imediatement ! Magique !

Ce petit tuto n’avait pas pour vocation de vous faire devenir un pro du useContext, mais de vous montrer un peu à quoi il peut servir. Mais avant de vous lancer corps et âme, je dois vous dire une chose importante.

UseContext / Redux

Soyons clair : le useContext ne remplacera jamais Redux. A premier lieu, on se dit que le useContext peut gérer un state globale comme redux et c’est (presque) vrai. Mais la grande différence est qu’à chaque modification d’une variable useContext, la méthode fait un render des composants, là où Redux ne le fait pas. Cela veut dire que si on a un site assez costaud, il faut éviter de trop utiliser les useContext car tous les composants seront « re-rendu » à chaque modification, et ça risque d’alourdir le site. Quand je dis tous les composants, je parle même de ceux qui ne sont pas encapsulé dans le composant du provider. Il parait qu’il existe un moyen de ne pas avoir ce défaut, et ce moyen se nomme « hookstate ». Prochain article en prévision.