Objekte im State aktualisieren
Der State kann jeden beliebigen JavaScript-Wert enthalten, einschließlich Objekten. Aber Objekte, die sich im React-State befinden, sollten nicht direkt verändert werden. Stattdessen muss beim Aktualisieren eines Objekts ein neues erstellt (oder eine Kopie eines bestehenden erstellt) und der State dann auf diese Kopie gesetzt werden.
Sie lernen
- Wie man ein Objekt im React-State korrekt aktualisiert
- Wie man ein verschachteltes Objekt aktualisiert, ohne es zu mutieren
- Was Unveränderlichkeit (Immutability) ist und wie man sie nicht bricht
- Wie man das Kopieren von Objekten mit Immer weniger repetitiv gestaltet
Was ist eine Mutation?
Im State kann jeder beliebige JavaScript-Wert gespeichert werden.
Bisher haben Sie mit Zahlen, Strings und Booleans gearbeitet. Diese Arten von JavaScript-Werten sind „immutable“, also unveränderlich oder „schreibgeschützt“. Sie können ein erneutes Rendering auslösen, um einen Wert zuersetzen:
Derx-State hat sich von0auf5geändert, aber dieZahl0selbsthat sich nicht geändert. Es ist nicht möglich, Änderungen an eingebauten primitiven Werten wie Zahlen, Strings und Booleans in JavaScript vorzunehmen.
Betrachten Sie nun ein Objekt im State:
Technisch gesehen ist es möglich, den Inhalt vondem Objekt selbstzu ändern.Dies wird als Mutation bezeichnet:
Obwohl Objekte im React-State technisch gesehen mutable sind, sollten Sie sie jedochso behandeln, als wärensie immutable – wie Zahlen, Booleans und Strings. Anstatt sie zu mutieren, sollten Sie sie immer ersetzen.
State als schreibgeschützt behandeln
Mit anderen Worten sollten Siejedes JavaScript-Objekt, das Sie in den State setzen, als schreibgeschützt behandeln.
Dieses Beispiel enthält ein Objekt im State, das die aktuelle Zeigerposition darstellt. Der rote Punkt soll sich bewegen, wenn Sie den Vorschaubereich berühren oder den Cursor darüber bewegen. Aber der Punkt bleibt an der Ausgangsposition:
Das Problem liegt in diesem Codeabschnitt.
Dieser Code modifiziert das Objekt, daspositionausdem vorherigen Renderingzugewiesen wurde. Aber ohne die State-Setter-Funktion zu verwenden, weiß React nicht, dass sich das Objekt geändert hat. Daher reagiert React nicht darauf. Es ist, als würde man versuchen, die Bestellung zu ändern, nachdem man bereits gegessen hat. Während das Mutieren des States in einigen Fällen funktionieren kann, empfehlen wir es nicht. Sie sollten den State-Wert, auf den Sie während eines Renderings Zugriff haben, als schreibgeschützt behandeln.
Um in diesem Fall tatsächlich einerneutes Rendering auszulösen, müssen SieeinneuesObjekt erstellen und es der State-Setter-Funktion übergeben:
MitsetPositionteilen Sie React Folgendes mit:
- Ersetze
positiondurch dieses neue Objekt - Und rendere diese Komponente erneut
Beachten Sie, wie der rote Punkt nun Ihrem Zeiger folgt, wenn Sie den Vorschaubereich berühren oder darüberfahren:
Objekte mit der Spread-Syntax kopieren
Im vorherigen Beispiel wird daspositionObjekt immer frisch aus der aktuellen Cursorposition erstellt. Oft möchten Sie jedochbestehendeDaten als Teil des neuen Objekts einbeziehen, das Sie erstellen. Beispielsweise möchten Sie möglicherweisenur einFeld in einem Formular aktualisieren, aber die vorherigen Werte für alle anderen Felder beibehalten.
Diese Eingabefelder funktionieren nicht, weil dieonChange-Handler den State mutieren:
Diese Zeile mutiert beispielsweise den State aus einem vorherigen Render:
Die zuverlässige Methode, um das gewünschte Verhalten zu erreichen, besteht darin, ein neues Objekt zu erstellen und es ansetPersonzu übergeben. Hier möchten Sie jedoch auchdie vorhandenen Daten hineinkopieren, da sich nur eines der Felder geändert hat:
Sie können die...Objekt-Spread-Syntax verwenden, damit Sie nicht jede Eigenschaft einzeln kopieren müssen.
Jetzt funktioniert das Formular!
Beachten Sie, dass Sie keine separate State-Variable für jedes Eingabefeld deklariert haben. Bei großen Formularen ist es sehr praktisch, alle Daten in einem Objekt gruppiert zu halten – solange Sie es korrekt aktualisieren!
Beachten Sie, dass die...Spread-Syntax „flach“ ist – sie kopiert Dinge nur eine Ebene tief. Das macht sie schnell, bedeutet aber auch, dass Sie sie mehr als einmal verwenden müssen, wenn Sie eine verschachtelte Eigenschaft aktualisieren möchten.
Aktualisieren eines verschachtelten Objekts
Betrachten Sie eine verschachtelte Objektstruktur wie diese:
Wenn Sieperson.artwork.cityaktualisieren möchten, ist klar, wie das mit Mutation geht:
Aber in React behandeln Sie State als unveränderlich! Umcityzu ändern, müssten Sie zuerst das neueartwork-Objekt erzeugen (vorab mit Daten aus dem vorherigen gefüllt) und dann das neueperson-Objekt, das auf das neueartworkzeigt:
Oder als einzelner Funktionsaufruf geschrieben:
Das wird etwas wortreich, funktioniert aber in vielen Fällen gut:
Kompakte Update-Logik mit Immer schreiben
Wenn Ihr State tief verschachtelt ist, sollten Sie in Betracht ziehen, ihnzu vereinfachen.Wenn Sie Ihre State-Struktur jedoch nicht ändern möchten, bevorzugen Sie vielleicht eine Abkürzung zu verschachtelten Spreads.Immerist eine beliebte Bibliothek, die es Ihnen ermöglicht, mit der bequemen, aber mutierenden Syntax zu schreiben und sich um die Erstellung der Kopien für Sie kümmert. Mit Immer sieht der Code, den Sie schreiben, so aus, als würden Sie "die Regeln brechen" und ein Objekt mutieren:
Aber im Gegensatz zu einer regulären Mutation überschreibt sie nicht den vorherigen Zustand!
So probieren Sie Immer aus:
- Führen Sie
npm install use-immeraus, um Immer als Abhängigkeit hinzuzufügen - Ersetzen Sie dann
import { useState } from 'react'durchimport { useImmer } from 'use-immer'
Hier ist das obige Beispiel, umgewandelt für Immer:
Beachten Sie, wie viel prägnanter die Event-Handler geworden sind. Sie könnenuseStateunduseImmerin einer einzelnen Komponente nach Belieben kombinieren. Immer ist eine hervorragende Möglichkeit, die Update-Handler prägnant zu halten, insbesondere wenn Ihr Zustand verschachtelt ist und das Kopieren von Objekten zu repetitivem Code führt.
Zusammenfassung
- Behandle alle Zustände in React als unveränderlich.
- Wenn du Objekte im Zustand speicherst, führt deren Mutation nicht zu Neu-Renderings und ändert den Zustand in vorherigen Render-„Momentaufnahmen“.
- Erstelle stattdessen eineneueVersion des Objekts und löse ein Neu-Rendering aus, indem du den Zustand darauf setzt.
- Du kannst die
{...obj, something: 'newValue'}Objekt-Spread-Syntax verwenden, um Kopien von Objekten zu erstellen. - Die Spread-Syntax ist flach: Sie kopiert nur eine Ebene tief.
- Um ein verschachteltes Objekt zu aktualisieren, musst du Kopien von der Stelle aus erstellen, die du änderst, bis nach oben.
- Um repetitives Kopier-Code zu reduzieren, verwende 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.
