Découvrez le Contexte dans React
Dans la famille des hooks, je veux maintenant celui qui nous permet d’utiliser simplement le Contexte de React !
Mais d’ailleurs, React Context, c’est quoi ? 🤔
Contexte est un moyen de partager simplement les props entre les composants. Contexte est natif à React et ne nécessite pas d'installer quoi que ce soit de plus.
Si vous avez suivi le cours précédent, vous avez vu comment passer de simples props entre les composants parents et enfants, et comment utiliser les props pour faire remonter le state, au chapitre “Partagez votre state entre différents composants". Mais est-ce que vous imaginez ce que ça pourrait donner dans une application complexe, où pour passer une prop à un composant enfant, vous devez le faire passer par des dizaines de composants parents qui n’ont eux-mêmes pas besoin de cette prop ?
À l’inverse, Contexte nous permet de récupérer simplement nos datas sans avoir à tout passer manuellement. Pour cela, on englobe le composant parent le plus haut dans l’arborescence de composants avec ce qu’on appelle un Provider
. Tous les composants enfants pourront alors se connecter au Provider
(littéralement en anglais, le “fournisseur”) et ainsi accéder aux props, sans avoir à passer par tous les composants intermédiaires. On dit que les composants enfants sont les Consumers
(consommateurs).
L’idée de passer simplement nos datas entre les composants est au cœur de nombreuses questions, afin que le code soit le plus performant et lisible possible. Vous verrez plus particulièrement des interrogations sur la manière de gérer le state de manière compréhensible et performante. On parle alors de State Management. Vous en avez déjà entendu parler ?
Avant les hooks, l’utilisation du Contexte était plus laborieuse, mais réjouissez-vous : vous avez maintenant le hook useContext
. 😍
Tirez profit du Contexte et de useContext
Maintenant que nous avons vu ce qu’était un Contexte, utilisons-le dès maintenant dans notre application Shiny !
Mettez en place un Contexte
Quelles données mettre dans le Contexte ?
La documentation de React dit que "le Contexte est conçu pour partager des données qui peuvent être considérées comme globales", et cite en exemple des données sur l’utilisateur actuellement authentifié, le thème, la langue utilisée, etc.
Nous allons commencer en douceur avec le contexte pour créer un Dark Mode (mode nuit) pour notre application. Vous savez, ce thème de couleurs plutôt sombres qui permet de reposer ses yeux devant l’écran.
Pour cela, nous allons créer un Footer dans lequel on ajoute un bouton. Dans /components
, on crée donc un fichier Footer/index.jsx
au fonctionnement assez basique :
import styled from 'styled-components'
import colors from '../../utils/style/colors'
const FooterContainer = styled.footer`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding-top: 60px;
`
const NightModeButton = styled.button`
background-color: transparent;
border: none;
cursor: pointer;
color: ${colors.secondary};
`
function Footer() {
return (
<FooterContainer>
<NightModeButton>Changer de mode</NightModeButton>
</FooterContainer>
)
}
export default Footer
Ce bouton dans notre Footer permettra de déclencher le dark mode de l’application. C’est maintenant le moment de créer notre Provider de Contexte pour le thème. Pour cela, on va créer un dossier dédié au Contexte dans utils/context
. On crée un fichier index.jsx
.
On commence par importer { createContext }
depuis react
, et initialiser notre Contexte pour le thème avec :
export const ThemeContext = createContext()
Et on utilise ensuite ThemeContext
:
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light')
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
}
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}
On a bien créé un composant qui nous permet de wrapper notre composant parent avec notre Provider
de thème. Le state de theme
et sa fonction pour le modifier, setTheme
, sont passés dans les values
. Ainsi, tous les composants enfants qui se retrouvent englobés par le Provider
vont pouvoir accéder à theme
et setTheme
.
C'est le moment d'utiliser notre Provider
au plus haut niveau où les composants devront pouvoir accéder au Contexte. On va donc le mettre dans index.jsx
à la racine de /src
. On a donc maintenant :
ReactDOM.render(
<React.StrictMode>
<Router>
<ThemeProvider>
<GlobalStyle />
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/survey/:questionNumber" element={<Survey />} />
<Route path="/results" element={<Results />} />
<Route path="/freelances" element={<Freelances />} />
<Route path="*" element={<Error />} />
</Routes>
<Footer />
</ThemeProvider>
</Router>
</React.StrictMode>,
document.getElementById('root')
);
Ça veut dire qu'on doit forcément mettre notre Provider
au niveau de notre router
? 🤔
Eh bien… Pas nécessairement ! Comme son nom l'indique, le Contexte nous sert à "contextualiser" nos datas. Certaines parties de l'application ont besoin d'être au courant d'une partie du state, alors qu'il n'y en a pas du tout besoin ailleurs. À vous de voir quelle utilisation est la plus adaptée.
Accédez à vos données avec useContext
Mais du coup, c’est quoi useContext
?
useContext
est un hook qui permet de se “brancher” depuis un composant enfant qui a été wrappé par un Provider
, et donc d’accéder simplement au state partagé.
Mettons-le en pratique dès maintenant.
On va d’abord utiliser le theme
pour effectuer une modification très visible : modifier le background-color
de toute notre application.
Auparavant, on avait notre GlobalStyle
dans notre fichier index.jsx
à la racine de /src
, mais on va le déplacer dans un fichier à part dans /utils/style/GlobalStyle.jsx
. On va également modifier GlobalStyle en composant fonction qui va nous permettre d'y utiliser des hooks (alors qu'avant c'était un simple styled component). On a donc :
function GlobalStyle() {
return <StyledGlobalStyle />
}
Puis on importe ThemeContext
et useContext
avec :
import { useContext } from 'react'
import { ThemeContext } from '../context/ThemeProvider'
Ce qui nous permet de récupérer le thème :
function GlobalStyle() {
const { theme } = useContext(ThemeContext)
return <StyledGlobalStyle isDarkMode={theme === 'dark'} />
}
... et donc de passer une prop isDarkMode
en fonction du thème activé.
Dans notre Style
, on l’utilise ainsi :
const StyledGlobalStyle = createGlobalStyle`
* {
font-family: 'Trebuchet MS', Helvetica, sans-serif;
}
body {
/* Ici cette syntaxe revient au même que
background-color: ${({ props }) =>
props.isDarkMode ? '#2F2E41' : 'white'};
*/
background-color: ${({ isDarkMode }) => (isDarkMode ? 'black' : 'white')};
margin: 0;
}
`
Et c’est parti pour la dernière pièce de notre puzzle : l’implémentation du bouton permettant de changer de mode. ☀️/🌙
On retourne donc dans notre Footer/index.jsx
. De la même manière que ce qu’on a fait juste avant, on importe ThemeContext
et useContext
.
Et on récupère notre action toggleTheme
et theme
avec :
const { toggleTheme, theme } = useContext(ThemeContext)
… qu’on peut utiliser juste en dessous :
function Footer() {
const { toggleTheme, theme } = useContext(ThemeContext)
return (
<FooterContainer>
<NightModeButton onClick={() => toggleTheme()}>
Changer de mode : {theme === 'light' ? '☀️' : '🌙'}
</NightModeButton>
</FooterContainer>
)
}
Et si on teste………
Ma-gni-fique ! 🤩
Bravo à vous : vous venez de créer un Contexte et vous l’avez utilisé avec useContext
pour créer un dark mode dans votre application. 🎉
Comme je vous le disais, vous êtes un chanceux : avant les hooks, le Contexte était plus compliqué à mettre en œuvre. Je vous fais une démonstration juste en dessous de comment ça se passait. 👇
Exercez-vous
Vous allez maintenant mettre ça en pratique. Pour cet exercice, vous devez utiliser le Contexte pour récupérer les résultats du questionnaire entre les pages.
Vous pouvez commencer l'exercice sur la branche P2C2-begin
. Dans context/index.jsx
, vous trouverez un SurveyProvider
prêt à être utilisé.
Dans /Survey/index.jsx
, vous trouverez la base de code pour stocker les réponses de l'utilisateur dans le Contexte, ainsi que des boutons permettant de répondre.
Comme dans le premier cours, on utilise encore une fois le spread operator pour stocker notre objet : vous souvenez-vous de l'immutabilité du state au chapitre “Partagez votre state entre différents composants” ? Pour cet exercice, vous allez devoir :
Utiliser votre SurveyProvider dans
src/index.jsx
.Sauvegarder les résultats dans votre state depuis
pages/Survey/index.jsx
.Récupérer les résultats dans
/pages/results.jsx
avecuseContext
, et les afficher dans un simpleconsole.log()
.
Sinon, vous trouverez tout le code de la solution sur la branche P2C2-solution
.
En résumé
Le Contexte est une fonctionnalité de React permettant de partager le state entre plusieurs composants parents et enfants, à l'aide d'un Provider.
useContext
est un hook permettant de se "brancher" très simplement au Contexte, et donc d'accéder au State partagé.L'utilisation du Contexte est une des méthodes de State Management, qui peut se cumuler avec d'autres méthodes, telles que l'utilisation de Redux.
Alors, comment ça s'est passé pour vous, l'utilisation du Contexte dans votre application avec le hook useContext
?
Dans le prochain chapitre, nous continuerons notre exploration des hooks avec la création de custom hooks (on pourrait dire en français des "hooks personnalisés"). Nous allons même créer notre propre hook useFetch
pour appeler une API. La classe, n'est-ce pas ? 😎