useReducer
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
- Utilisation
- Dépannage
- J’ai dispatché une action, mais la console m’affiche l’ancienne valeur
- J’ai dispatché une action, mais l’écran ne se met pas à jour
- Une partie de l’état de mon réducteur devient undefined après le dispatch
- Tout l’état de mon réducteur devient undefined après le dispatch
- J’obtiens l’erreur “Too many re-renders”
- Mon réducteur ou ma fonction d’initialisation s’exécute deux fois
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ètreinitqui suit.initoptionnelle : la fonction d’initialisation qui doit renvoyer l’état initial. Si elle n’est pas spécifiée, l’état initial est défini avecinitialArg. Autrement, il est défini en appelantinit(initialArg).
Valeur renvoyée
useReducer renvoie un tableau avec exactement deux valeurs :
- L’état courant. Lors du premier rendu, il est défini avec
init(initialArg)ouinitialArg(s’il n’y a pas d’argumentinit). - La fonction
dispatchqui vous permet de mettre à jour l’état vers une valeur différente et ainsi redéclencher un rendu.
Limitations et points à noter
useReducerest 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
dispatcha 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étypepermettant 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
dispatchne met à jour l’état que pour le prochain rendu. Si vous lisez une variable d’état après avoir appelé la fonction dedispatch, vous aurez encore l’ancienne valeur qui était à l’écran avant cet appel. -
Si la nouvelle valeur fournie est identique au
stateactuel, déterminé par une comparaison avecObject.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 utiliserflushSync.
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 :
- L’état actuel de cette variable d’état, défini initialement avec l’état initial que vous avez fourni.
- La fonction
dispatchqui 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.