React vous offre deux façons de maintenir des données vivantes à l'intérieur d'un composant : useState et useRef. À première vue, ils semblent similaires. Tous deux renvoient quelque chose que vous pouvez lire, tous deux survivent aux re-rendus, et tous deux vous permettent de mémoriser une valeur d'un clic à l'autre. Cependant, si vous choisissez le mauvais, vous vous retrouverez soit avec un écran qui refuse de se mettre à jour, soit avec une parade interminable de rendus inutiles. Le choix n'est pas une question de syntaxe. C'est une question de savoir si React a besoin d'être au courant.

Ce que chaque hook fait réellement

useState est le canal de communication officiel de React avec un composant. Lorsque vous l'appelez, vous recevez une valeur et une fonction de mise à jour (setter). React suit cette valeur en tant que partie intégrante de l'identité du composant. Lorsque le setter est déclenché, React dit : « Quelque chose a changé », et planifie un nouveau rendu pour que l'écran puisse se mettre à jour.

useRef, en revanche, n'est rien de plus qu'un simple objet JavaScript doté d'une propriété current. React promet de vous remettre exactement la même référence d'objet à chaque rendu. Il ne surveille pas ce qui se trouve à l'intérieur. La mutation de someRef.current se produit silencieusement. React ne réagira pas.

Ce silence est précisément l'objectif. Les refs sont une soupape de sécurité, pas un remplacement de l'état.

La fracture du rendu

Si vous modifiez l'état, le composant est re-rendu. C'est le comportement que la plupart des débutants attendent, et c'est exactement ce que vous voulez lorsque les nouvelles données doivent apparaître à l'écran. Un compteur, un champ de formulaire, une liste d'utilisateurs récupérée — si l'utilisateur le voit, cela appartient probablement à l'état. Tout le flux de données de React est construit autour de l'idée que les changements d'état notifient le moteur de rendu pour synchroniser le DOM.

Si vous modifiez une ref, rien ne se passe visuellement. La variable est mise à jour immédiatement et de manière synchrone, mais le composant ne se re-rend pas. Cela rend les refs idéales pour les valeurs qui soutiennent le fonctionnement interne du composant sans faire partie du rendu visuel. Pensez aux IDs de minuteurs, aux instantanés de props précédentes ou aux handles DOM directs. L'interface utilisateur ne se soucie pas de l'ID de l'intervalle qui pilote l'autoplay d'un carrousel ; elle se soucie uniquement de savoir quelle diapositive est visible. L'ID de l'intervalle doit être placé dans une ref.

Quand l'état est le bon outil

Utilisez useState chaque fois qu'une valeur fait partie de la surface de votre interface utilisateur.

Les champs de saisie en sont l'exemple évident. Si un utilisateur tape une adresse e-mail et que vous devez la valider et afficher un message d'erreur sous le champ, cette chaîne d'e-mail a besoin d'un état. La logique de validation et la bannière d'erreur dépendent toutes deux de la dernière valeur, et React ne sait mettre à jour la bannière que parce que l'état a déclenché un re-rendu.

Les compteurs et les commutateurs sont un autre incontournable. Un bouton qui incrémente un score, un indicateur d'ouverture/fermeture de modal, un index d'onglet — tout cela passe par l'état car le résultat du rendu change en même temps que la valeur. Même les valeurs dérivées, comme une liste filtrée qui dépend d'une chaîne de recherche, commencent généralement par un état car la valeur source est visible pour l'utilisateur.

Il existe également une nuance de timing qui mérite d'être comprise. Les mises à jour d'état sont asynchrones et regroupées (batched). Si vous appelez setCount(count + 1) trois fois à l'intérieur d'un seul gestionnaire d'événements, React ne rend pas trois fois. Il les regroupe en une seule mise à jour. La variable count à l'intérieur de votre fonction en cours reste également obsolète jusqu'au prochain rendu. Ce regroupement est une fonctionnalité. Il permet de garder les applications rapides. Mais cela signifie que vous ne pouvez pas vous attendre à ce que la variable d'état reflète la nouvelle valeur dès la ligne suivante.

Quand les refs vous sauvent la mise

Utilisez useRef pour la tuyauterie, pas