Le hook useReducer: une alternative structurée à useState
Pourquoi et quand utiliser useReducer?
Bien que useState soit idéal pour gérer des états simples, il montre rapidement ses limites dès que la logique devient plus riche ou que plusieurs variables doivent évoluer de manière coordonnée. On se retrouve alors avec une multiplication de setters de states, des conditions dispersées dans le composant et une logique difficile à suivre ou à maintenir. Plus l’état se complexifie, plus le code perd en lisibilité et en cohérence, ce qui augmente le risque d’erreurs et rend le débogage moins intuitif. Dans ces cas-là, il devient essentiel d’adopter une méthode plus structurée, ce qui conduit naturellement vers useReducer, une solution qui clarifie et centralise la gestion d’état.
On rappelle souvent que useReducer convient mieux aux états complexes, imbriqués ou nécessitant plusieurs mises à jour coordonnées. Toutefois, la notion de "complexité" reste subjective et dépend largement de l’appréciation du développeur.
useReducer: logique d’état basée sur les actions
L’approche de useReducer repose sur une logique d’état guidée par des actions plutôt que par des mises à jour directes comme avec useState. Au lieu de modifier l’état morceau par morceau, on décrit ce qui doit se produire à travers une action nommée, ce qui rend la logique plus explicite et plus facile à suivre. Chaque action représente une intention claire (par exemple, ajouter un élément, réinitialiser un formulaire, changer un statut...) et l’état évolue en fonction de ces intentions plutôt que de manipulations dispersées.
Cette manière de penser l’état apporte une meilleure structuration et une cohérence accrue, surtout lorsque les transitions deviennent nombreuses ou interdépendantes. Donc, useReducer encourage une gestion d’état plus organisée, centrée sur les actions plutôt que sur les valeurs elles‑mêmes.
Imaginons que l'on souhaite incrémenter, décrémenter ou réinitialiser un nombre que l'on affiche dans un composant Text. Pour cela, on va prévoir trois boutons, chacun chargé d'entamer l'action souhaitée.
Je vais écrire le code d'un seul coup puis expliquerai ses différents recoins:
import { View, StyleSheet, Text, Button } from 'react-native';
import { useReducer} from 'react';
export default function App() {
const reducer=(currentValue, action)=>{
switch(action){
case "+": return currentValue+1
case "-": return currentValue-1
case "0": return 0
}
}
const [nbr,dispatchNbr]=useReducer(reducer,0)
return (
<View style={styles.container}>
<Button title="INC"
onPress={()=>dispatchNbr("+")} />
<Button title="DEC"
onPress={()=>dispatchNbr("-")} />
<Button title="INIT"
onPress={()=>dispatchNbr("0")} />
<Text>{nbr}</Text>
</View>
);
}
const styles=StyleSheet.create({
container:{
flex:1,
justifyContent:"center",
alignItems:"center"
}
})
Le reducer: la fonction qui décide comment l’état change
Un
reducer est une fonction qui reçoit l’état actuel et une action, puis renvoie le nouvel état en appliquant la logique appropriée. C’est lui qui centralise et structure la manière dont l’état évolue dans un composant utilisant le hook useReducer.
const reducer=(currentValue, action)=>{
switch(action){
case "+": return currentValue+1
case "-": return currentValue-1
case "0": return 0
}
}
Comme on le voit dans le code ci-dessus, le reducer reçoit deux éléments:
- currentValue: l’état ou valeur actuelle du state, c’est‑à‑dire la dernière valeur validée par React avant l’action en cours.
- action: une information qui décrit ce qu’on veut faire (incémenter, décrémenter ou Réinitialiser le state)
Son rôle est simple: retourner le nouvel état (nouvelle valeur du state) en fonction de l’action reçue.
Le reducer ne modifie jamais l’état directement (contrairement au setter de useState), mais il se contente de renvoyer une nouvelle valeur.
useReducer: création de l’état et du dispatcher
useReducer est un hook qui permet de gérer un state en utilisant une logique basée sur des actions. Il accepte deux arguments: le reducer décrit plus haut et la valeur initiale du state:
const [nbr,dispatchNbr]=useReducer(reducer,0)
Dans le code précédent, useReducer renvoie deux éléments :
- nbr: l’état actuel (initialisé à 0)
- dispatchNbr: une fonction spéciale appelée dispatcher. Elle sert à envoyer une action au reducer. Donc, c’est lui qui déclenche la mise à jour de l’état.
Le dispatcher en action
<Button title="INC" onPress={()=>dispatchNbr("+")} />
<Button title="DEC" onPress={()=>dispatchNbr("-")} />
<Button title="INIT" onPress={()=>dispatchNbr("0")} />
Chaque bouton envoie une action au reducer à travers le dispatcher (ici nommé dispatchNbr). Le reducer décidera ensuite de la nouvelle valeur du state.
- dispatchNbr("+"): demande d’incrémentation
- dispatchNbr("-"): demande de décrémentation
- dispatchNbr("0"): demande de réinitialisation
Donc, le reducer reçoit l’action, calcule le nouvel état ce qui déclenche la mise à jour de l’interface.
Enfin, le choix entre useState et useReducer dépend avant tout de la vision du développeur et de la manière dont il conçoit la gestion de l’état. Certains privilégient la simplicité et la lisibilité immédiate de useState, surtout pour des états locaux et peu structurés. D’autres préfèrent la logique plus explicite et centralisée de useReducer qui s’intègre mieux lorsqu’on veut organiser l’évolution de l’état autour d’actions clairement définies.
En pratique, les deux approches sont valides, mais c’est la philosophie d’organisation, la complexité du composant et la préférence du développeur qui orientent le choix.