OUJOOD.COM
Introduction
Le Hook `useReducer` est utilisé pour gérer des états complexes dans React, notamment lorsqu'il y a plusieurs actions ou transformations possibles sur l'état. Contrairement à `useState`, qui est adapté aux états simples, `useReducer` permet de centraliser la logique d'état dans une fonction de réduction, rendant ainsi le code plus maintenable et scalable.
Dans ce chapitre, nous allons explorer :
- Qu'est-ce que `useReducer` ?
- Comment créer et utiliser un reducer.
- Des exemples pratiques pour illustrer son fonctionnement.
1. Qu'est-ce que `useReducer` ?
`useReducer` est un Hook React qui prend deux arguments principaux :
- Une fonction de réduction : Définit comment l'état doit être mis à jour en fonction des actions reçues.
- Une valeur initiale : Représente l'état initial avant toute action.
Il retourne un tableau contenant deux éléments :
- L'état actuel : Correspond à l'état géré par le reducer.
- Une fonction dispatch : Permet d'envoyer des actions au reducer pour mettre à jour l'état.
2. Création d'un Reducer Simple
Voici un exemple simple où nous utilisons `useReducer` pour gérer un compteur avec plusieurs actions possibles :
import React, { useReducer } from 'react'; import { createRoot } from 'react-dom/client'; // Fonction de réduction function reducer(étatActuel, action) { switch (action.type) { case 'INCREMENTER': return { ...étatActuel, compte: étatActuel.compte + 1 }; case 'DECREMENTER': return { ...étatActuel, compte: étatActuel.compte - 1 }; case 'RESET': return { ...étatActuel, compte: 0 }; default: throw new Error('Action inconnue'); } } // Composant principal function Compteur() { const [état, dispatch] = useReducer(reducer, { compte: 0 }); // Initialisation de l'état return ( <div> <h1>Compteur : {état.compte}</h1> <button onClick={() => dispatch({ type: 'INCREMENTER' })}>Incrémenter</button> <button onClick={() => dispatch({ type: 'DECREMENTER' })}>Décrémenter</button> <button onClick={() => dispatch({ type: 'RESET' })}>Réinitialiser</button> </div> ); } const root = createRoot(document.getElementById('root')); root.render(<Compteur />);
Explication détaillée ligne par ligne :
-
import React, { useReducer } from 'react';
:- Nous importons React ainsi que le Hook
useReducer
. useReducer
est utilisé pour gérer des états complexes via une fonction de réduction.
- Nous importons React ainsi que le Hook
-
function reducer(étatActuel, action) { ... }
:- Cette fonction de réduction définit comment l'état doit être mis à jour en fonction des actions reçues.
- Elle prend deux arguments :
étatActuel
(l'état actuel) etaction
(l'action à exécuter).
-
switch (action.type) { ... }
:- Cette structure conditionnelle (
switch
) permet de gérer différentes actions possibles. - Ici, nous avons trois types d'actions :
INCREMENTER
,DECREMENTER
, etRESET
.
- Cette structure conditionnelle (
-
case 'INCREMENTER': return { ...étatActuel, compte: étatActuel.compte + 1 };
:- Ce cas incrémente la valeur de l'état
compte
de1
. { ...étatActuel }
crée une copie de l'état actuel pour éviter de le modifier directement.
- Ce cas incrémente la valeur de l'état
-
case 'DECREMENTER': return { ...étatActuel, compte: étatActuel.compte - 1 };
:- Ce cas décrémente la valeur de l'état
compte
de1
.
- Ce cas décrémente la valeur de l'état
-
case 'RESET': return { ...étatActuel, compte: 0 };
:- Ce cas réinitialise la valeur de l'état
compte
à0
.
- Ce cas réinitialise la valeur de l'état
-
const [état, dispatch] = useReducer(reducer, { compte: 0 });
:- Cette ligne initialise un état appelé
état
avec une valeur initiale{ compte: 0 }
. dispatch
est une fonction utilisée pour envoyer des actions au reducer.
- Cette ligne initialise un état appelé
-
<button onClick={() => dispatch({ type: 'INCREMENTER' })}>Incrémenter</button>
:- Ce bouton appelle la fonction
dispatch
avec une action{ type: 'INCREMENTER' }
. - Cela met à jour l'état via la fonction de réduction définie précédemment.
- Ce bouton appelle la fonction
-
const root = createRoot(document.getElementById('root'));
:- Cette ligne crée une instance de racine React appelée
root
. document.getElementById('root')
spécifie l'élément HTML où le composant sera inséré.
- Cette ligne crée une instance de racine React appelée
-
root.render(<Compteur />);
:- Rend le composant
Compteur
dans l'élément HTML ayant l'idroot
.
- Rend le composant
3. Comparaison entre `useState` et `useReducer`
Bien qu'ils puissent sembler similaires, `useState` et `useReducer` ont des usages distincts. Voici leurs différences principales :
Aspect | useState | useReducer |
---|---|---|
Fonctionnement | Gère un seul état simple et ses mises à jour. | Gère des états complexes via une fonction de réduction centralisée. |
Utilisation | Idéal pour des états simples comme un compteur ou un texte. | Parfait pour des états complexes impliquant plusieurs transformations ou actions. |
Complexité | Facile à utiliser mais limité pour des états avancés. | Peut sembler plus complexe au départ, mais simplifie la gestion d'états complexes. |
4. Exemple Avancé : Gestion d'un Panier d'Achat avec useReducer en React
Imaginons que vous souhaitez créer un panier d'achat avec des fonctionnalités comme l'ajout, la suppression et la mise à jour des produits. Voici comment cela peut être fait avec `useReducer` :
Explication du Code :
- Utilisation de
useReducer
:- Permet de gérer un état complexe à travers une fonction de réduction.
- Remplace
useState
pour des mises à jour plus structurées.
- Fonction
reducer
:- Gère trois actions : ajouter un produit, supprimer un produit et vider le panier.
- Met à jour l'état en fonction du type d'action reçu.
- Actions disponibles :
AJOUTER_PRODUIT
: Ajoute un produit au panier.SUPPRIMER_PRODUIT
: Supprime un produit du panier.VIDER_PANIER
: Vide complètement le panier.
import React, { useReducer } from 'react'; import { createRoot } from 'react-dom/client'; // Fonction de réduction pour le panier function reducer(étatActuel, action) { switch (action.type) { case 'AJOUTER_PRODUIT': return { ...étatActuel, produits: [...étatActuel.produits, action.payload] }; case 'SUPPRIMER_PRODUIT': return { ...étatActuel, produits: étatActuel.produits.filter((_, index) => index !== action.payload) }; case 'VIDER_PANIER': return { ...étatActuel, produits: [] }; default: throw new Error('Action inconnue'); } } // Composant principal function Panier() { const [état, dispatch] = useReducer(reducer, { produits: [] }); function ajouterProduit() { dispatch({ type: 'AJOUTER_PRODUIT', payload: { nom: 'Nouveau produit', prix: 10 } }); } function supprimerProduit(index) { dispatch({ type: 'SUPPRIMER_PRODUIT', payload: index }); } function viderPanier() { dispatch({ type: 'VIDER_PANIER' }); } return ( <div> <h1>Panier d'achat</h1> <button onClick={ajouterProduit}>Ajouter un produit</button> <button onClick={viderPanier}>Vider le panier</button> <ul> {état.produits.map((produit, index) => ( <li key={index}> {produit.nom} - {produit.prix} € <button onClick={() => supprimerProduit(index)}>Supprimer</button> </li> ))} </ul> </div> ); } const root = createRoot(document.getElementById('root')); root.render(<Panier />);
Explication détaillée ligne par ligne :
-
function reducer(étatActuel, action) { ... }
:- Cette fonction de réduction gère les transformations de l'état du panier selon les actions reçues.
-
case 'AJOUTER_PRODUIT': return { ...étatActuel, produits: [...étatActuel.produits, action.payload] };
:- Ce cas ajoute un nouveau produit au tableau
produits
de l'état. action.payload
représente le produit à ajouter.
- Ce cas ajoute un nouveau produit au tableau
-
case 'SUPPRIMER_PRODUIT': return { ...étatActuel, produits: étatActuel.produits.filter((_, index) => index !== action.payload) };
:- Ce cas supprime un produit du tableau
produits
en filtrant l'élément correspondant à l'index spécifié dansaction.payload
.
- Ce cas supprime un produit du tableau
-
case 'VIDER_PANIER': return { ...étatActuel, produits: [] };
:- Ce cas réinitialise le tableau
produits
à un tableau vide.
- Ce cas réinitialise le tableau
-
const [état, dispatch] = useReducer(reducer, { produits: [] });
:- Cette ligne initialise un état appelé
état
avec une valeur initiale contenant un tableau vide{ produits: [] }
. dispatch
est utilisée pour envoyer des actions au reducer.
- Cette ligne initialise un état appelé
-
dispatch({ type: 'AJOUTER_PRODUIT', payload: { nom: 'Nouveau produit', prix: 10 } });
:- Cette ligne envoie une action
AJOUTER_PRODUIT
au reducer avec un objet produit comme payload.
- Cette ligne envoie une action
-
supprimerProduit(index)
:- Cette fonction envoie une action
SUPPRIMER_PRODUIT
au reducer avec l'index du produit à supprimer.
- Cette fonction envoie une action
-
viderPanier()
:- Cette fonction envoie une action
VIDER_PANIER
au reducer pour réinitialiser le panier.
- Cette fonction envoie une action
Pourquoi utiliser useReducer
?
- Facilite la gestion d'un état complexe.
- Offre une structure claire et évolutive.
- Idéal pour gérer des états qui impliquent plusieurs sous-actions.
Améliorations possibles :
- Ajouter un champ pour personnaliser le nom et le prix des produits.
- Afficher le total des prix dans le panier.
- Persister les données dans
localStorage
.
Conclusion
Vous avez maintenant appris à utiliser le Hook `useReducer` pour gérer des états complexes dans vos applications React. En centralisant la logique d'état dans une fonction de réduction, vous pouvez rendre votre code plus maintenable et évolutif, surtout pour des cas d'utilisation avancés comme la gestion d'un panier d'achat.
Prochain chapitre : useCallback