Contactez-nous

Mise en place de `useReducer` (état initial, fonction reducer, dispatch)

Apprenez étape par étape comment configurer et utiliser le hook useReducer de React : définir l'état initial, écrire la fonction reducer et utiliser dispatch.

Introduction : Les trois piliers de `useReducer`

Pour utiliser efficacement le hook `useReducer` dans vos composants fonctionnels React, vous devez comprendre et mettre en place trois éléments essentiels : l'état initial qui définit le point de départ, la fonction reducer qui encapsule toute la logique de mise à jour, et l'appel au hook lui-même qui vous donne accès à l'état actuel et à la fonction `dispatch` pour déclencher les changements.

Cette section vous guidera à travers chacune de ces étapes, en illustrant le processus avec un exemple concret pour clarifier comment ces pièces s'assemblent pour gérer l'état de manière structurée.

Etape 1 : Définir l'Etat Initial (`initialState`)

Tout comme avec `useState`, vous devez fournir une valeur initiale pour l'état géré par `useReducer`. Cette valeur, souvent appelée initialState, représente l'état de votre composant lors de son premier rendu, avant qu'aucune action n'ait été dispatchée.

L'état initial peut être une valeur simple (nombre, chaîne, booléen) ou, plus fréquemment avec `useReducer`, un objet contenant plusieurs propriétés liées.

// Exemple 1: Etat initial simple pour un compteur
const initialCounterState = { count: 0 };

// Exemple 2: Etat initial plus complexe pour un fetch de données
const initialFetchState = {
  data: null,
  isLoading: false,
  error: null,
};
Initialisation Paresseuse (Lazy Initialization)

Similaire à `useState`, `useReducer` permet également une initialisation paresseuse. Au lieu de passer directement l'état initial, vous pouvez passer une fonction (appelée `init` function). Cette fonction ne sera exécutée qu'une seule fois au montage pour calculer l'état initial. C'est utile si le calcul de l'état initial est coûteux. Le hook `useReducer` accepte alors trois arguments : `reducer`, `initialArg`, et `init`.

function calculateInitialState(initialValue) {
  console.log("Calcul initial coûteux...");
  // ... logique complexe ...
  return { value: initialValue * 2 };
}

// Dans le composant:
// const [state, dispatch] = useReducer(myReducer, props.initialValue, calculateInitialState);
// 'calculateInitialState' sera appelée avec 'props.initialValue' une seule fois.

Etape 2 : Définir la Fonction Reducer (`reducer`)

C'est le coeur de la logique de `useReducer`. La fonction reducer détermine comment l'état change en réponse à une action. Rappelez-vous, elle doit être une fonction pure qui prend deux arguments :

  • state : L'état actuel.
  • action : L'objet action qui a été dispatché (contenant généralement un type et parfois un payload).

Elle doit retourner le nouvel état (sans muter l'ancien état).

// Exemple de reducer pour gérer l'état du fetch de données
function fetchReducer(state, action) {
  switch (action.type) {
    case 'FETCH_START':
      // Retourne un nouvel objet état
      return { ...state, isLoading: true, error: null }; 
    case 'FETCH_SUCCESS':
      return { ...state, isLoading: false, data: action.payload, error: null };
    case 'FETCH_ERROR':
      return { ...state, isLoading: false, error: action.payload };
    case 'RESET':
      // Peut utiliser l'état initial (si disponible dans la portée) ou une version définie ici
      return { data: null, isLoading: false, error: null }; 
    default:
      // Important: retourner l'état inchangé pour les actions non reconnues
      return state;
  }
}

La structure switch sur action.type est très courante car elle permet de gérer clairement les différentes transitions d'état possibles.

Etape 3 : Appeler `useReducer` et obtenir `state` et `dispatch`

Maintenant, à l'intérieur de votre composant fonctionnel, vous appelez le hook `useReducer` en lui passant votre fonction reducer et votre état initial (ou les arguments pour l'initialisation paresseuse).

Le hook retourne un tableau contenant deux éléments, que vous déstructurez généralement :

  • state : La valeur actuelle de l'état (initialement, initialState). Cette variable contient la dernière valeur retournée par votre reducer.
  • dispatch : La fonction spéciale que vous utiliserez pour envoyer (dispatcher) des actions à votre reducer afin de déclencher des mises à jour d'état. React garantit que la fonction `dispatch` a une identité stable et ne changera pas entre les rendus.
import React, { useReducer, useEffect } from 'react';

// Supposons que initialFetchState et fetchReducer sont définis comme ci-dessus

function DataComponent() {
  // Appel du hook useReducer
  const [state, dispatch] = useReducer(fetchReducer, initialFetchState);

  // Utilise state pour afficher l'UI
  const { data, isLoading, error } = state;

  useEffect(() => {
    // Utilise dispatch pour signaler le début du fetch
    dispatch({ type: 'FETCH_START' });

    fetch('/api/mydata')
      .then(response => {
        if (!response.ok) throw new Error('Erreur réseau');
        return response.json();
      })
      .then(fetchedData => {
        // Utilise dispatch pour signaler le succès
        dispatch({ type: 'FETCH_SUCCESS', payload: fetchedData });
      })
      .catch(fetchError => {
        // Utilise dispatch pour signaler l'erreur
        dispatch({ type: 'FETCH_ERROR', payload: fetchError.message });
      });
  }, []); // Exécute une seule fois au montage

  if (isLoading) {
    return 
Chargement...
; } if (error) { return
Erreur : {error}
; } return (

Données :

{JSON.stringify(data, null, 2)}
);}export default DataComponent;

Etape 4 : Utiliser `dispatch`

La fonction `dispatch` est le seul moyen d'initier une mise à jour de l'état géré par `useReducer`. Vous l'appelez en lui passant un objet action. Cet objet action est ensuite transmis comme second argument à votre fonction reducer.

Comme vu dans l'exemple précédent, vous appelez `dispatch` depuis vos gestionnaires d'événements (onClick sur le bouton Reset) ou depuis des `useEffect` (pour gérer les différentes étapes d'un fetch asynchrone) en fournissant l'objet action approprié ({ type: 'RESET' }, { type: 'FETCH_START' }, { type: 'FETCH_SUCCESS', payload: data }, etc.).

Conclusion : Un flux structuré pour l'état

La mise en place de `useReducer` implique de définir clairement votre état initial, d'écrire une fonction reducer pure et immuable qui gère toutes les transitions d'état, et d'utiliser le hook pour obtenir l'état actuel et la fonction `dispatch`. En appelant `dispatch` avec des actions descriptives, vous déclenchez l'exécution du reducer qui calcule le nouvel état, assurant un flux de données prévisible et centralisé.

Ce processus, bien que légèrement plus verbeux que `useState` au départ, offre une structure et une clarté précieuses pour gérer les logiques d'état complexes, facilitant la maintenance, le débogage et les tests.