Comprendre et utiliser le Hook useState en React
Le hook useState
permet de gérer l’état d’une propriété d’un composant.
Définition du hook useState
- Il prend en paramètre une valeur initiale de l’état.
- Il retourne un tableau de deux éléments :
- La propriété d’état actuelle.
- Une fonction setter qui met à jour l’état de cette propriété.
import { useState } from "react";
export default function App() {
const [user, setUser] = useState({
firstName: "John",
lastName: "Doe",
age: 18,
});
const incrementAge = () => {
setUser({ ...user, age: user.age + 1 });
};
return (
<>
<p>
Age de {user.firstName} {user.lastName} : {user.age}
</p>
<button onClick={incrementAge}>Incrémenter l'age</button>
</>
);
}
À chaque fois que la fonction setUser
est appelée, le composant est à nouveau rendu.
Le principe d'immuabilité de l'état en React
En React, l'état doit être traité comme immuable, c'est-à-dire qu'il ne doit jamais être modifié directement. Lorsqu'on appelle setState
, React compare la nouvelle valeur de l'état avec l'ancienne. Si ces deux valeurs sont identiques, React ignore la mise à jour, car il considère que rien n'a changé. Cela est simple à comprendre pour les valeurs primitives (comme les nombres ou les chaînes de caractères), mais la situation est différente pour les structures de données non primitives, comme les objets et les tableaux.
React effectue une comparaison de référence pour ces structures non primitives, et non une comparaison de contenu. Ainsi, si un objet ou un tableau est modifié directement, leur référence en mémoire ne change pas, même si leur contenu a été altéré. Dans ce cas, React ne détectera pas la modification et n'actualisera pas le composant, car la référence est restée la même.
C'est pourquoi, lorsque vous mettez à jour un objet ou un tableau dans l'état, il est crucial de créer une nouvelle instance de cet objet ou tableau. Cela garantit que la référence en mémoire change et que React détecte correctement la mise à jour de l'état.
Par exemple, pour ajouter un élément à un tableau, vous pouvez utiliser l'opérateur de décomposition (spread operator) pour créer un nouveau tableau à partir de l'ancien :
setItems([...items, newItem]);
De cette manière, React pourra comparer la nouvelle référence du tableau à l'ancienne et reconnaîtra qu'une mise à jour est nécessaire. Cette approche garantit que le principe d'immuabilité est respecté.
Problème des mises à jour d'état successives avec useState et comment le résoudre
Si la fonction incrementAge
appelle le setUser
plusieurs fois de suite, cela ne va pas incrémenter l’âge plusieurs fois comme on pourrait le penser. Par exemple :
const incrementAge = () => {
setUser({ ...user, age: user.age + 1 });
setUser({ ...user, age: user.age + 1 });
setUser({ ...user, age: user.age + 1 });
};
Dans ce cas, si l’âge initial est 18, après un clic, l’âge sera 19 et non 21. Cela s’explique par le fait que chaque appel à setUser
utilise la valeur de user.age
au moment où le composant a été rendu. Cela équivalent à :
const incrementAge = () => {
setUser({ ...user, age: 18 + 1 });
setUser({ ...user, age: 18 + 1 });
setUser({ ...user, age: 18 + 1 });
};
Pour mettre à jour l’état plusieurs fois correctement, vous pouvez passer une fonction au setter qui prend l’état actuel en paramètre et retourne le nouvel état :
const incrementAge = () => {
setUser((prevUser) => ({ ...prevUser, age: prevUser.age + 1 }));
setUser((prevUser) => ({ ...prevUser, age: prevUser.age + 1 }));
setUser((prevUser) => ({ ...prevUser, age: prevUser.age + 1 }));
};