Structurez les états complexes avec un reducer pur qui centralise les transitions et élimine les combinaisons impossibles.
Ouvrir cette leçon dans KodokonDès qu'un composant orchestre plusieurs valeurs d'état qui évoluent ensemble - un statut de requête, des données, une erreur -, multiplier les useState disperse la logique de transition dans chaque gestionnaire d'événements. Vous connaissez le symptôme : trois setters à appeler dans le bon ordre, et tôt ou tard un état incohérent, comme un statut à success accompagné d'une erreur non nulle. useReducer inverse la responsabilité : l'état devient un objet unique, et chaque changement passe par une action interprétée par une fonction centrale, le reducer.
function requestReducer(state, action) {
switch (action.type) {
case "start":
return { status: "loading", data: null, error: null };
case "resolve":
return {
status: "success",
data: action.data,
error: null,
};
case "reject":
return {
status: "error",
data: null,
error: action.error,
};
default:
throw new Error("Unknown action: " + action.type);
}
}Le reducer est une fonction pure : il reçoit l'état courant et une action, puis retourne le prochain état, sans effet de bord. Les bénéfices sont immédiats : les transitions sont explicites et exhaustives, les états impossibles disparaissent à la source (chaque case reconstruit un objet cohérent), et la logique se lit en un seul endroit au lieu d'être éparpillée. Signal d'alarme utile : si vous copiez-collez la même séquence de setState à deux endroits, c'est probablement un reducer qui manque.
function UserProfile() {
const [state, dispatch] = useReducer(requestReducer, {
status: "idle",
data: null,
error: null,
});
async function load(id) {
dispatch({ type: "start" });
try {
const res = await fetch("/api/users/" + id);
const data = await res.json();
dispatch({ type: "resolve", data });
} catch (error) {
dispatch({ type: "reject", error });
}
}
return <button onClick={() => load(1)}>Charger</button>;
}Le choix entre useState et useReducer n'est pas une affaire de goût. Gardez useState pour des valeurs simples et indépendantes ; passez à useReducer quand le prochain état dépend du précédent ou quand plusieurs champs changent de concert. Deux atouts supplémentaires : le troisième argument offre une initialisation paresseuse (useReducer(reducer, arg, init) n'exécute init qu'au premier rendu), et React garantit que dispatch conserve la même identité d'un rendu à l'autre - vous pouvez le passer aux enfants sans useCallback.