useReducer est un Hook React qui vous permet d’ajouter un réducteur à votre composant.

const [state, dispatch] = useReducer(reducer, initialArg, init?)

Référence

useReducer(reducer, initialArg, init?)

Appelez useReducer au niveau racine de votre composant pour gérer son état avec un réducteur.

import { useReducer } from 'react';

function reducer(state, action) {
// ...
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

Voir d’autres exemples ci-dessous.

Paramètres

  • reducer : la fonction de réduction qui spécifie comment votre état est mis à jour. Elle doit être pure, prendre l’état et l’action en paramètres et renvoyer le prochain état. L’état et l’action peuvent être de n’importe quels types.
  • initialArg : la valeur à partir de laquelle l’état est calculé. Elle peut être de n’importe quel type. La façon dont l’état initial est calculé dépend du paramètre init qui suit.
  • init optionnelle : la fonction d’initialisation qui doit renvoyer l’état initial. Si elle n’est pas spécifiée, l’état initial est défini avec initialArg. Autrement, il est défini en appelant init(initialArg).

Valeur renvoyée

useReducer renvoie un tableau avec exactement deux valeurs :

  1. L’état courant. Lors du premier rendu, il est défini avec init(initialArg) ou initialArg (s’il n’y a pas d’argument init).
  2. La fonction dispatch qui vous permet de mettre à jour l’état vers une valeur différente et ainsi redéclencher un rendu.

Limitations et points à noter

  • useReducer est un Hook, vous ne pouvez donc l’appeler qu’au niveau racine de votre composant ou de vos propres Hooks. Vous ne pouvez pas l’appeler dans des boucles ou des conditions. Si vous avez besoin de le faire, extrayez un nouveau composant et déplacez-y l’état.
  • La fonction dispatch a une identité stable, elle ne figure donc généralement pas dans les dépendances des Effets, mais l’inclure n’entraînera pas un déclenchement d’Effet superflu. Si le linter vous permet de l’omettre sans erreurs, c’est que cette omission est sans danger. Apprenez-en davantage sur l’allègement des dépendances d’Effets
  • En Mode Strict, React appellera deux fois votre réducteur et votre fonction d’initialisation afin de vous aider à détecter des impuretés accidentelles. Ce comportement est limité au développement et n’affecte pas la production. Si votre réducteur et votre fonction d’initialisation sont pures (ce qui devrait être le cas), ça n’impactera pas votre logique. Le résultat de l’un des appels est ignoré.

Fonction dispatch

La fonction dispatch renvoyée par useReducer vous permet de mettre à jour l’état avec une valeur différente et de déclencher un rendu. Le seul paramètre à passer à la fonction dispatch est l’action :

const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
dispatch({ type: 'incremented_age' });
// ...

React définira le prochain état avec le résultat de l’appel de la fonction reducer que vous avez fournie avec le state courant et l’action que vous avez passée à dispatch.

Paramètres

  • action : l’action réalisée par l’utilisateur. Elle peut être de n’importe quelle nature. Par convention, une action est généralement un objet avec une propriété type permettant son identification et, optionnellement, d’autres propriétés avec des informations additionnelles.

Valeur renvoyée

Les fonctions dispatch ne renvoient rien.

Limitations

  • La fonction dispatch ne met à jour l’état que pour le prochain rendu. Si vous lisez une variable d’état après avoir appelé la fonction de dispatch, vous aurez encore l’ancienne valeur qui était à l’écran avant cet appel.

  • Si la nouvelle valeur fournie est identique au state actuel, déterminé par une comparaison avec Object.is, React sautera le nouveau rendu du composant et de ses enfants. C’est une optimisation. React aura peut-être quand même besoin d’appeler votre composant avant d’en ignorer le résultat, mais ça ne devrait pas affecter votre code.

  • React met à jour l’état par lots. Il met à jour l’écran une fois que tous les gestionnaires d’événements ont été exécutés et ont appelé leurs fonctions set. Ça évite les rendus multiples à la suite d’un événement unique. Dans les rares cas où vous devez forcer React à mettre à jour l’écran prématurément, par exemple pour accéder au DOM, vous pouvez utiliser flushSync.


Utilisation

Ajouter un réducteur à un composant

Appelez useReducer au niveau racine de votre composant pour gérer l’état avec un réducteur.

import { useReducer } from 'react';

function reducer(state, action) {
// ...
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

useReducer renvoie un tableau avec exactement deux éléments :

  1. L’état actuel de cette variable d’état, défini initialement avec l’état initial que vous avez fourni.
  2. La fonction dispatch qui vous permet de le modifier en réponse à une interaction.

Pour mettre à jour ce qui est à l’écran, appelez dispatch avec un objet, nommé action, qui représente ce que l’utilisateur a fait :

function handleClick() {
dispatch({ type: 'incremented_age' });
}

React passera l’état actuel et l’action à votre fonction de réduction. Votre réducteur calculera et renverra le nouvel état. React sauvera ce nouvel état, fera le rendu avec celui-ci puis mettra à jour l’interface utilisateur.

import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'incremented_age' })
      }}>
        Incrémenter l’âge
      </button>
      <p>Bonjour ! Vous avez {state.age} ans.</p>
    </>
  );
}

useReducer est très similaire à useState, mais il vous permet de déplacer la logique de mise à jour de l’état depuis les gestionnaires d’événements vers une seule fonction à l’extérieur de votre composant. Apprenez-en davantage sur comment choisir entre useState et useReducer.


Écrire la fonction de réduction

Une fonction de réduction est déclarée ainsi :

function reducer(state, action) {
// ...
}

Ensuite, vous devez la remplir avec le code qui va calculer et renvoyer le prochain état. Par convention, il est courant de l’écrire en utilisant une instruction switch. Pour chaque case du switch, calculez et renvoyez un état suivant.

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}

Les actions peuvent prendre n’importe quelle forme. Par convention, il est courant de passer des objets avec une propriété type pour identifier l’action. Ils doivent juste inclure les informations nécessaires au réducteur pour calculer le prochain état.

function Form() {
const [state, dispatch] = useReducer(reducer, { name: 'Clara', age: 42 });

function handleButtonClick() {
dispatch({ type: 'incremented_age' });
}

function handleInputChange(e) {
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// ...

Les noms des types d’actions sont locaux à votre composant. Chaque action décrit une seule interaction, même si ça amène à modifier plusieurs fois la donnée. L’état peut avoir n’importe quelle forme, mais ce sera généralement un objet ou un tableau.

Lisez Extraire la logique d’état dans un réducteur pour en apprendre davantage.

Piège

L’état est en lecture seule. Ne modifiez pas les objets ou les tableaux dans l’état :

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// 🚩 Ne modifiez pas l’objet dans l’état de cette façon :
state.age = state.age + 1;
return state;
}

Au lieu de ça, renvoyez toujours de nouveaux objets depuis votre réducteur :

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ✅ Renvoyez plutôt un nouvel objet
return {
...state,
age: state.age + 1
};
}

Lisez nos pages Mettre à jour les objets d’un état et Mettre à jour les tableaux d’un état pour en apprendre davantage.

Exemples simples de useReducer

Exemple 1 sur 3 ·
Formulaire (objet)

Dans cet exemple, le réducteur gère un état sous forme d’objet ayant deux champs : name et age.

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name