Ich erstelle eine App, in der der Benutzer sein eigenes Formular entwerfen kann. Geben Sie beispielsweise den Namen des Felds und die Details der anderen Spalten an, die enthalten sein sollen.
Die Komponente ist als JSFiddle verfügbar hier .
Mein Ausgangszustand sieht so aus:
var DynamicForm = React.createClass({
getInitialState: function() {
var items = {};
items[1] = { name: 'field 1', populate_at: 'web_start',
same_as: 'customer_name',
autocomplete_from: 'customer_name', title: '' };
items[2] = { name: 'field 2', populate_at: 'web_end',
same_as: 'user_name',
autocomplete_from: 'user_name', title: '' };
return { items };
},
render: function() {
var _this = this;
return (
<div>
{ Object.keys(this.state.items).map(function (key) {
var item = _this.state.items[key];
return (
<div>
<PopulateAtCheckboxes this={this}
checked={item.populate_at} id={key}
populate_at={data.populate_at} />
</div>
);
}, this)}
<button onClick={this.newFieldEntry}>Create a new field</button>
<button onClick={this.saveAndContinue}>Save and Continue</button>
</div>
);
}
Ich möchte den Status aktualisieren, wenn der Benutzer einen der Werte ändert, aber es fällt mir schwer, das richtige Objekt zu finden:
var PopulateAtCheckboxes = React.createClass({
handleChange: function (e) {
item = this.state.items[1];
item.name = 'newName';
items[1] = item;
this.setState({items: items});
},
render: function() {
var populateAtCheckbox = this.props.populate_at.map(function(value) {
return (
<label for={value}>
<input type="radio" name={'populate_at'+this.props.id} value={value}
onChange={this.handleChange} checked={this.props.checked == value}
ref="populate-at"/>
{value}
</label>
);
}, this);
return (
<div className="populate-at-checkboxes">
{populateAtCheckbox}
</div>
);
}
});
Wie soll ich basteln this.setState
, damit es aktualisiert wird items[1].name
?
javascript
reactjs
state
Martins
quelle
quelle
Antworten:
Sie können den
update
Unveränderlichkeitshelfer dafür verwenden :Wenn es Ihnen nicht wichtig ist, Änderungen an diesem Element in einer
shouldComponentUpdate()
Lebenszyklusmethode mithilfe von zu erkennen===
, können Sie den Status direkt bearbeiten und das erneute Rendern der Komponente erzwingen. Dies entspricht praktisch der Antwort von @limelights Ziehen eines Objekts aus dem Status und Bearbeiten.Nachbearbeitungszusatz:
In der Lektion zur Kommunikation einfacher Komponenten aus dem Reaktionstraining finden Sie ein Beispiel für die Übergabe einer Rückruffunktion von einem übergeordneten Element an eine untergeordnete Komponente, die eine Statusänderung auslösen muss.
quelle
Da dieser Thread viele Fehlinformationen enthält, können Sie dies wie folgt ohne Hilfsbibliotheken tun:
Sie können die Schritte 2 und 3 kombinieren, wenn Sie möchten:
Oder Sie können das Ganze in einer Zeile erledigen:
Hinweis: Ich habe
items
ein Array erstellt. OP hat ein Objekt verwendet. Die Konzepte sind jedoch dieselben.Sie können sehen, was in Ihrem Terminal / Ihrer Konsole los ist:
quelle
items[0] === clone[0]
Bit in meinem Terminal-Beispiel unten. Dreifach=
prüft, ob sich die Objekte auf dasselbe beziehen.items.findIndex()
sollte kurze Arbeit daraus machen.Falscher Weg!
Wie viele bessere Entwickler in den Kommentaren betonten : Mutation des Staates ist falsch!
Ich habe eine Weile gebraucht, um das herauszufinden. Oben funktioniert, aber es nimmt die Kraft von React weg.
componentDidUpdate
Dies wird beispielsweise nicht als Update angezeigt, da es direkt geändert wird.Also der richtige Weg wäre:
quelle
items[1].role = e.target.value
Ruft der mutierende Zustand nicht direkt auf?Um tief verschachtelte Objekte / Variablen im Status von React zu ändern, werden normalerweise drei Methoden verwendet: Vanilla JavaScript
Object.assign
, Unveränderlichkeitshelfer undcloneDeep
von Lodash .Es gibt auch viele andere weniger beliebte Bibliotheken von Drittanbietern, um dies zu erreichen, aber in dieser Antwort werde ich nur diese drei Optionen behandeln. Es gibt auch einige zusätzliche Vanilla-JavaScript-Methoden, wie z. B. das Verbreiten von Arrays (siehe z. B. die Antwort von @ mpen), die jedoch nicht sehr intuitiv, einfach zu verwenden und in der Lage sind, alle Zustandsmanipulationssituationen zu bewältigen.
Wie unzählige Male in den oben abgestimmten Kommentaren zu den Antworten erwähnt wurde, deren Autoren eine direkte Mutation des Staates vorschlagen: Tu das einfach nicht . Dies ist ein allgegenwärtiges Reaktionsmuster, das unweigerlich zu unerwünschten Konsequenzen führt. Lerne den richtigen Weg.
Vergleichen wir drei weit verbreitete Methoden.
Angesichts dieser Zustandsobjektstruktur:
Sie können die folgenden Methoden verwenden, um das Innerste zu aktualisieren
inner
den Wert Felds ohne den Rest des Status zu beeinflussen.1. Object.assign von Vanilla JavaScript
Beachten Sie, dass Object.assign kein tiefes Klonen ausführt , da nur Eigenschaftswerte kopiert werden . Daher wird das, was es tut, als flaches Kopieren bezeichnet (siehe Kommentare).
Damit dies funktioniert, sollten wir nur die Eigenschaften primitiver Typen (
outer.inner
) bearbeiten, dh Zeichenfolgen, Zahlen, Boolesche Werte.In diesem Beispiel erstellen wir eine neue Konstante (
const newOuter...
),Object.assign
die ein leeres Objekt ({}
) erstellt,outer
object ({ inner: 'initial value' }
) hinein kopiert und dann ein anderes Objekt{ inner: 'updated value' }
darüber kopiert .Auf diese Weise enthält die neu erstellte
newOuter
Konstante am Ende einen Wert von,{ inner: 'updated value' }
da dieinner
Eigenschaft überschrieben wurde. DiesnewOuter
ist ein brandneues Objekt, das nicht mit dem Objekt im Status verknüpft ist, sodass es nach Bedarf mutiert werden kann. Der Status bleibt unverändert und wird erst geändert, wenn der Befehl zum Aktualisieren ausgeführt wird.Der letzte Teil besteht darin, den
setOuter()
Setter zu verwenden, um das Originalouter
im Status durch ein neu erstelltesnewOuter
Objekt zu ersetzen (nur der Wert ändert sich, der Eigenschaftsnameouter
nicht).Stellen Sie sich nun vor, wir haben einen tieferen Zustand wie
state = { outer: { inner: { innerMost: 'initial value' } } }
. Wir könnten versuchen, dasnewOuter
Objekt zu erstellen und es mit demouter
Inhalt des StatusObject.assign
zu fülleninnerMost
, können jedoch den Wert nicht in dieses neu erstelltenewOuter
Objekt kopieren , dainnerMost
es zu tief verschachtelt ist.Sie können weiterhin kopieren
inner
, wie im obigen Beispiel, aber da es sich nun um ein Objekt und nicht um ein Grundelement handelt, wird die Referenz von stattdessen auf dasnewOuter.inner
kopiertouter.inner
, was bedeutet, dass das lokalenewOuter
Objekt direkt mit dem Objekt im Status verknüpft ist .Das bedeutet, dass in diesem Fall Mutationen des lokal erstellten Objekts
newOuter.inner
dasouter.inner
Objekt (im Status) direkt beeinflussen , da sie tatsächlich dasselbe geworden sind (im Speicher des Computers).Object.assign
Daher funktioniert es nur, wenn Sie eine relativ einfache einstufige Tiefenzustandsstruktur mit innersten Elementen haben, die Werte vom primitiven Typ enthalten.Wenn Sie tiefere Objekte (2. Ebene oder mehr) haben, die Sie aktualisieren sollten, verwenden Sie diese nicht
Object.assign
. Sie riskieren eine direkte Mutation des Zustands.2. Lodashs CloneDeep
Lodashs cloneDeep ist viel einfacher zu bedienen. Es führt ein tiefes Klonen durch , daher ist es eine robuste Option, wenn Sie einen ziemlich komplexen Status mit mehrstufigen Objekten oder Arrays haben. Nur
cloneDeep()
die State-Eigenschaft der obersten Ebene, mutieren Sie den geklonten Teil nach Belieben und kehren SiesetOuter()
zum State zurück.3. Unveränderlichkeitshelfer
immutability-helper
auf eine ganz neue Ebene dauert es, und die kühle daran ist , dass es nicht nur in$set
dem Zustand Artikel Werte, sondern auch$push
,$splice
,$merge
(etc.) sie. Hier ist eine Liste der verfügbaren Befehle .Randnotizen
Beachten Sie auch hier, dass
setOuter
nur die Eigenschaften der ersten Ebene des Statusobjekts (outer
in diesen Beispielen) geändert werden, nicht die tief verschachtelten (outer.inner
). Wenn es sich anders verhalten würde, würde diese Frage nicht existieren.Welches ist das richtige für Ihr Projekt?
Wenn Sie keine externen Abhängigkeiten verwenden möchten oder können und eine einfache Statusstruktur haben , bleiben Sie bei
Object.assign
.Wenn Sie einen riesigen und / oder komplexen Zustand manipulieren,
cloneDeep
ist Lodash eine kluge Wahl.Wenn Sie erweiterte Funktionen benötigen , dh wenn Ihre Statusstruktur komplex ist und Sie alle Arten von Operationen ausführen müssen, versuchen Sie
immutability-helper
, es ist ein sehr erweitertes Tool, das für die Statusmanipulation verwendet werden kann.... oder müssen Sie das wirklich überhaupt tun?
Wenn Sie komplexe Daten im Status von React halten, ist dies möglicherweise ein guter Zeitpunkt, um über andere Möglichkeiten nachzudenken, wie Sie damit umgehen können. Das Festlegen komplexer Statusobjekte in React-Komponenten ist keine einfache Operation, und ich empfehle dringend, über verschiedene Ansätze nachzudenken.
Höchstwahrscheinlich sollten Sie Ihre komplexen Daten nicht in einem Redux-Speicher aufbewahren, sie dort mithilfe von Reduzierungen und / oder Sagen festlegen und mithilfe von Selektoren darauf zugreifen.
quelle
Object.assign
führt keine tiefe Kopie durch. Sagen Sie, wenna = {c: {d: 1}}
undb = Object.assign({}, a)
, dann führen Sie ausb.c.d = 4
, danna.c.d
ist mutiert.1
des innersten Objekts (a.c.d
) wird mutiert. Wenn Sie jedoch den Nachfolger der ersten Ebeneb
wie folgt neu zuweisen :b.c = {f: 1}
wird der entsprechende Teil vona
nicht mutiert (es bleibt{d: 1}
). Trotzdem ein guter Fang, ich werde die Antwort sofort aktualisieren.shallow copy
und keindeep copy
. Es ist leicht zu verwechseln, wasshallow copy
bedeutet. Inshallow copy
,a !== b
aber für jeden Schlüssel voma
a[key] === b[key]
Object.assign
in der Antwort.JSON.parse(JSON.stringify(object))
ist auch eine Variante für Deep Clone. Die Leistung ist jedoch schlechter als lodashcloneDeep
. messethat.net/Benchmarks/Show/2751/0/…Ich hatte das gleiche Problem. Hier ist eine einfache Lösung, die funktioniert!
quelle
{
geschweifte Klammern anstelle von Klammern verwendete, die ich behoben hatteLaut der React-Dokumentation zu setState ist die Verwendung,
Object.assign
wie in anderen Antworten vorgeschlagen, hier nicht ideal. Aufgrund der Art dessetState
asynchronen Verhaltens können nachfolgende Aufrufe mit dieser Technik frühere Aufrufe überschreiben und unerwünschte Ergebnisse verursachen.In den React-Dokumenten wird stattdessen empfohlen, das Updater-Formular zu verwenden,
setState
das im vorherigen Status ausgeführt wird. Beachten Sie, dass Sie beim Aktualisieren eines Arrays oder Objekts ein neues Array oder Objekt zurückgeben müssen, da React erfordert, dass der Status unveränderlich bleibt. Wenn Sie den Spread-Operator der ES6-Syntax zum flachen Kopieren eines Arrays verwenden und eine Eigenschaft eines Objekts an einem bestimmten Index des Arrays erstellen oder aktualisieren, sieht dies folgendermaßen aus:quelle
Holen Sie sich zuerst das gewünschte Element, ändern Sie die gewünschten Elemente für dieses Objekt und setzen Sie es wieder auf den Status. Die Art und Weise, wie Sie den Status verwenden, indem Sie nur ein Objekt übergeben,
getInitialState
wäre viel einfacher, wenn Sie ein Schlüsselobjekt verwenden würden.quelle
Uncaught TypeError: Cannot read property 'items' of null
.getInitialState
.items
ist nirgendwo definiert.item
der eigenenthis.state.items[1]
Variablen des Status einen Verweis zu . Dann ändern Sieitem
(item.name = 'newName'
) und mutieren so den Status direkt, was dringend empfohlen wird. In Ihrem Beispiel müssen Sie nicht einmal aufrufenthis.setState({items: items})
, da der Status bereits direkt mutiert ist.Mutieren Sie den Staat nicht. Dies kann zu unerwarteten Ergebnissen führen. Ich habe meine Lektion gelernt! Arbeiten Sie immer mit einer Kopie / einem Klon,
Object.assign()
ist eine gute:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
quelle
items
in deinem Beispiel? Meinten Siethis.state.items
oder etwas anderes?items[1] = item;
sollte eine Zeile stehenitems = this.state.items;
. Passen Sie auf, dass mein Javascript rostig ist und ich lerne, auf meinEs ist wirklich einfach.
Ziehen Sie zuerst das gesamte Elementobjekt aus dem Status, aktualisieren Sie den Teil des Elementobjekts wie gewünscht und versetzen Sie das gesamte Elementobjekt über setState wieder in den Status.
quelle
Da keine der oben genannten Optionen für mich ideal war, habe ich letztendlich die Karte verwendet:
quelle
Mutationsfrei:
quelle
newItems
eingestellt ist.fo of
oder dieforEach
Karte ist die schnellste.Fand dies überraschend schwierig und keine der ES6-Verbreitungsmagien schien wie erwartet zu funktionieren. Verwenden Sie eine solche Struktur, um gerenderte Elementeigenschaften für Layoutzwecke abzurufen.
In diesem vereinfachten Beispiel wurde festgestellt, dass die
update
Methode vonimmutability-helper
die einfachste ist:wie von https://github.com/kolodny/immutability-helper#computed-property-names angepasst
des zu aktualisierenden Array-Mitglieds ist ein stärker verschachteltes komplexes Objekt. Verwenden Sie die entsprechende Deep-Copy-Methode die auf der Komplexität basiert.
Es gibt sicherlich bessere Möglichkeiten, mit Layoutparametern umzugehen, aber hier geht es darum, wie man mit Arrays umgeht. Die relevanten Werte für jedes untergeordnete Element könnten auch außerhalb von ihnen berechnet werden. Ich fand es jedoch bequemer, containerState weiterzugeben, damit die untergeordneten Elemente nach Belieben Eigenschaften abrufen und das übergeordnete Statusarray an ihrem angegebenen Index aktualisieren können.
quelle
Verwenden Sie die Array-Karte mit Pfeilfunktion in einer Zeile
quelle
Verwenden Sie das Ereignis ein
handleChange
, um das geänderte Element herauszufinden und es dann zu aktualisieren. Dazu müssen Sie möglicherweise eine Eigenschaft ändern, um sie zu identifizieren und zu aktualisieren.Siehe Geige https://jsfiddle.net/69z2wepo/6164/
quelle
Ich würde die Änderung des Funktionshandles verschieben und einen Indexparameter hinzufügen
an die dynamische Formularkomponente und übergeben Sie sie als Requisite an die PopulateAtCheckboxes-Komponente. Während Sie Ihre Elemente durchlaufen, können Sie einen zusätzlichen Zähler (im folgenden Code als Index bezeichnet) einfügen, der wie unten gezeigt an die Handle-Änderung übergeben wird
Schließlich können Sie Ihren Änderungslistener wie unten gezeigt anrufen
quelle
Wenn Sie nur einen Teil von ändern müssen
Array
, haben Sie eine Reaktionskomponente mit dem Status auf.Es ist am besten,
red-one
dasArray
wie folgt zu aktualisieren :quelle
newArray
? meinst dunewItems
? Wenn Sie dies tun, würde das den Staat danach nicht mit nur einem Gegenstand verlassen?newItems
demstate
Objekt eine neue Eigenschaft hinzugefügt, und die vorhandeneitems
Eigenschaft wird nicht aktualisiert .oder wenn Sie eine dynamisch generierte Liste haben und den Index nicht kennen, sondern nur den Schlüssel oder die ID haben:
quelle
Versuchen Sie es mit Code:
quelle
Das Befolgen des Codes hat mein langweiliges Gehirn geschont. Entfernen des Objekts und Ersetzen durch das aktualisierte
quelle
Wie wäre es, eine andere Komponente zu erstellen (für ein Objekt, das in das Array aufgenommen werden muss) und Folgendes als Requisiten zu übergeben?
Hier kann {index} basierend auf der Position übergeben werden, an der diese SubObjectForm verwendet wird.
und setSubObjectData kann so etwas sein.
In SubObjectForm kann this.props.setData bei Datenänderung wie unten angegeben aufgerufen werden.
quelle
quelle
item = Object.assign({}, item, {name: 'newName'});
@ JonnyBuchanans Antwort funktioniert einwandfrei, jedoch nur für Array-Statusvariablen. Wenn die Statusvariable nur ein einzelnes Wörterbuch ist, gehen Sie wie folgt vor:
Sie können
[input]
durch den Feldnamen Ihres Wörterbuchs unde.target.value
durch dessen Wert ersetzen . Dieser Code führt den Aktualisierungsjob beim Eingabeänderungsereignis meines Formulars aus.quelle
Versuchen Sie dies, es wird definitiv funktionieren, in einem anderen Fall habe ich es versucht, aber es hat nicht funktioniert
quelle
und Ändern aus Textfeld onChange-Ereignis hinzufügen
quelle