Mettre à jour les objets dans l'état
L'état peut contenir n'importe quelle valeur JavaScript, y compris des objets. Mais vous ne devriez pas modifier directement les objets que vous stockez dans l'état React. Au lieu de cela, lorsque vous souhaitez mettre à jour un objet, vous devez en créer un nouveau (ou faire une copie d'un existant), puis définir l'état pour utiliser cette copie.
Vous apprendrez
- Comment mettre à jour correctement un objet dans l'état React
- Comment mettre à jour un objet imbriqué sans le muter
- Ce qu'est l'immuabilité, et comment ne pas la briser
- Comment rendre la copie d'objets moins répétitive avec Immer
Qu'est-ce qu'une mutation ?
Vous pouvez stocker n'importe quelle valeur JavaScript dans l'état.
Jusqu'à présent, vous avez travaillé avec des nombres, des chaînes de caractères et des booléens. Ces types de valeurs JavaScript sont « immuables », c'est-à-dire non modifiables ou « en lecture seule ». Vous pouvez déclencher un nouveau rendu pourremplacerune valeur :
L'étatxest passé de0 à 5, mais lenombre0lui-mêmen'a pas changé. Il n'est pas possible de modifier les valeurs primitives intégrées comme les nombres, les chaînes de caractères et les booléens en JavaScript.
Considérons maintenant un objet dans l'état :
Techniquement, il est possible de changer le contenu del'objet lui-même.Cela s'appelle une mutation :
Cependant, bien que les objets dans l'état React soient techniquement mutables, vous devez les traitercomme s'ilsétaient immuables — comme les nombres, les booléens et les chaînes de caractères. Au lieu de les muter, vous devez toujours les remplacer.
Traiter l'état comme étant en lecture seule
En d'autres termes, vous deveztraiter tout objet JavaScript que vous placez dans l'état comme étant en lecture seule.
Cet exemple contient un objet dans l'état pour représenter la position actuelle du pointeur. Le point rouge est censé se déplacer lorsque vous touchez ou déplacez le curseur sur la zone de prévisualisation. Mais le point reste à la position initiale :
Le problème vient de ce morceau de code.
Ce code modifie l'objet assigné àpositiondepuisle rendu précédent.Mais sans utiliser la fonction de définition d'état, React ne sait pas que l'objet a changé. Donc React ne fait rien en réponse. C'est comme essayer de changer la commande après avoir déjà mangé le repas. Bien que muter l'état puisse fonctionner dans certains cas, nous ne le recommandons pas. Vous devez traiter la valeur d'état à laquelle vous avez accès lors d'un rendu comme étant en lecture seule.
Pour réellementdéclencher un nouveau rendudans ce cas,créez unnouvelobjet et passez-le à la fonction de définition d'état :
AvecsetPosition, vous dites à React :
- Remplacer
positionpar ce nouvel objet - Et rendre à nouveau ce composant
Remarquez comment le point rouge suit maintenant votre pointeur lorsque vous touchez ou survolez la zone de prévisualisation :
Copier des objets avec la syntaxe de décomposition
Dans l'exemple précédent, l'objetpositionest toujours créé à partir de la position actuelle du curseur. Mais souvent, vous voudrez inclure des donnéesexistantesdans le nouvel objet que vous créez. Par exemple, vous souhaiterez peut-être mettre à jourun seulchamp dans un formulaire, tout en conservant les valeurs précédentes pour tous les autres champs.
Ces champs de saisie ne fonctionnent pas car les gestionnairesonChangemutent l'état :
Par exemple, cette ligne mute l'état d'un rendu précédent :
La méthode fiable pour obtenir le comportement souhaité est de créer un nouvel objet et de le passer àsetPerson. Mais ici, vous voulez aussicopier les données existantes dedanscar un seul des champs a changé :
Vous pouvez utiliser la syntaxe de décomposition...d'objetpour ne pas avoir à copier chaque propriété séparément.
Maintenant, le formulaire fonctionne !
Notez que vous n'avez pas déclaré de variable d'état séparée pour chaque champ de saisie. Pour les formulaires volumineux, regrouper toutes les données dans un objet est très pratique—à condition de le mettre à jour correctement !
Notez que la syntaxe de décomposition...est « superficielle »—elle ne copie les éléments qu'à un seul niveau de profondeur. Cela la rend rapide, mais cela signifie aussi que si vous voulez mettre à jour une propriété imbriquée, vous devrez l'utiliser plus d'une fois.
Mettre à jour un objet imbriqué
Considérez une structure d'objet imbriqué comme celle-ci :
Si vous vouliez mettre à jourperson.artwork.city, il est clair comment le faire avec une mutation :
Mais dans React, vous traitez l'état comme immuable ! Pour changercity, vous devez d'abord produire le nouvel objetartwork(pré-rempli avec les données du précédent), puis produire le nouvel objetpersonqui pointe vers le nouvelartwork:
Ou, écrit comme un seul appel de fonction :
Cela devient un peu verbeux, mais cela fonctionne bien dans de nombreux cas :
Écrire une logique de mise à jour concise avec Immer
Si votre état est profondément imbriqué, vous pourriez envisager del'aplatir.Mais, si vous ne voulez pas changer la structure de votre état, vous pourriez préférer un raccourci aux spreads imbriqués.Immerest une bibliothèque populaire qui vous permet d'écrire en utilisant la syntaxe pratique mais mutante et se charge de produire les copies pour vous. Avec Immer, le code que vous écrivez semble "enfreindre les règles" et muter un objet :
Mais contrairement à une mutation classique, elle ne remplace pas l'état précédent !
Pour essayer Immer :
- Exécutez
npm install use-immerpour ajouter Immer comme dépendance - Puis remplacez
import { useState } from 'react'parimport { useImmer } from 'use-immer'
Voici l'exemple ci-dessus converti pour utiliser Immer :
Remarquez à quel point les gestionnaires d'événements sont devenus plus concis. Vous pouvez mélanger et assortiruseStateetuseImmerdans un seul composant autant que vous le souhaitez. Immer est un excellent moyen de garder les gestionnaires de mise à jour concis, surtout s'il y a de l'imbrication dans votre état et que la copie d'objets conduit à du code répétitif.
Récapitulatif
- Traitez tout état dans React comme immuable.
- Lorsque vous stockez des objets dans l'état, les modifier ne déclenchera pas de rendus et changera l'état dans les "instantanés" des rendus précédents.
- Au lieu de modifier un objet, créez unenouvelleversion de celui-ci, et déclenchez un nouveau rendu en définissant l'état sur cette version.
- Vous pouvez utiliser la syntaxe de décomposition d'objet
{...obj, something: 'newValue'}pour créer des copies d'objets. - La syntaxe de décomposition est superficielle : elle ne copie qu'à un niveau de profondeur.
- Pour mettre à jour un objet imbriqué, vous devez créer des copies à partir de l'endroit que vous modifiez, et ce jusqu'au niveau supérieur.
- Pour réduire le code de copie répétitif, utilisez Immer.
Try out some challenges
Challenge 1 of 3:Fix incorrect state updates #
This form has a few bugs. Click the button that increases the score a few times. Notice that it does not increase. Then edit the first name, and notice that the score has suddenly “caught up” with your changes. Finally, edit the last name, and notice that the score has disappeared completely.
Your task is to fix all of these bugs. As you fix them, explain why each of them happens.
