useState
est un hook React qui permet de stocker une variable (état) dans un composant fonctionnel et de la mettre à jour.
On l'utilise lorsqu'on veut gérer un état local dans un composant fonctionnel.
On ne peut pas mettre à jour de cette manière :
let count = 0;
count = count + 1;
React a un fonctionnement particulier, c'est lui qui s'occupe de réafficher (rerender) correctement le composant en cas de mise à jour. Sans useState
, React ne peut pas détecter le changement de count, count sera mis à jour certes mais la vue ne le sera pas.
import React from "react";
export function App() {
let count = 0;
const increment = () => {
count = count + 1;
console.log("count", count); // count augmente
};
console.log("render"); // mais le composant ne rerender pas
return (
<div className="App">
<h1>Count : {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
Voici ce qu'on reçoit en console :
render
count 1
count 2
count 3
Le composant ne rerender pas entre chaque mise à jour de count donc la vue reste toujours au point de départ.
const [state, setState] = useState(valeurInitiale);
useState
accepte en premier argument la valeur initiale de l'état. Cette valeur peut être une fonction, un objet, un tableau, une chaîne de caractères ou un nombre.
useState
retourne un tableau qui contient en premier élément l'état à jour et en deuxième élément la fonction qui met à jour cet état.
Dans notre exemple, on peut l'utiliser comme ceci :
import React, { useState } from "react";
export function App() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div className="App">
<h1>Count : {count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
Il faut savoir que React ne met pas à jour l'état dans une même fonction. Imaginons qu'on veuille mettre à jour plusieurs fois count
dans la même fonction :
const increment = () => {
setCount(count + 1); // count : 0
setCount(count + 1); // count : 0
setCount(count + 1); // count : 0
};
On voit que le changement n'est pas opéré. Voici comment faire pour éviter ce problème :
const increment = () => {
setCount((prevCount) => prevCount + 1); // count : 1
setCount((prevCount) => prevCount + 1); // count : 2
setCount((prevCount) => prevCount + 1); // count : 3
};
Avec un callback, on peut récupérer l'état à jour. Je vous conseille de toujours privilégier ce format pour éviter d'éventuels bugs.
Autre exemple, si on veut utiliser count
dans une condition :
const increment = () => {
setCount((prevCount) => prevCount + 1);
if (count > 0) {
// ne rentre pas dans la condition car count est toujours 0
}
};
La mise à jour ne sera pas non plus opérée ici. On peut donc écrire comme cela pour être sûr d'avoir la donnée souhaitée :
const increment = () => {
setCount((prevCount) => prevCount + 1);
const newCount = count + 1;
if (newCount > 0) {
// rentre dans la condition car count est maintenant à 1
}
};
Imaginons qu'on veuille incrémenter l'âge d'une personne comme ceci :
export function App() {
const [person, setPerson] = useState({ name: "John", age: 50 });
const increment = () => {
person.age += 1;
setPerson(person);
};
return (
<div className="App">
<h1>Count : {person.age}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
Ici, on ne met pas correctement à jour l'objet. En React, l'état doit être immuable; c'est-à-dire qu'on ne doit pas modifier l'état directement. On doit toujours faire une copie de l'état avant de le mettre à jour.
export function App() {
const [person, setPerson] = useState({ name: "John", age: 50 });
const increment = () => {
setPerson((prevPerson) => ({ ...prevPerson, age: prevPerson.age + 1 }));
};
return (
<div className="App">
<h1>Count : {person.age}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
Voici la bonne pratique. On crée un nouvel objet, puis on utilise le spread operator (...
) pour y extraire chaque propriété de person
avant de modifier seulement l'âge. On entoure l'objet de parenthèses pour le retourner correctement.
Voilà pour l'utilisation de useState
. Vous avez appris l'essentiel des bonnes pratiques de ce hook : utiliser un callback dans setState
et faire une copie de l'état avant de le mettre à jour. La mise à jour des tableaux et des tableaux d'objets est plus fastidieuse mais les mêmes principes s'appliquent.