So aktualisieren Sie verschachtelte Statuseigenschaften in React

390

Ich versuche, meinen Status mithilfe einer verschachtelten Eigenschaft wie folgt zu organisieren:

this.state = {
   someProperty: {
      flag:true
   }
}

Aber den Status so aktualisieren,

this.setState({ someProperty.flag: false });

funktioniert nicht Wie kann das richtig gemacht werden?

Alex Yong
quelle
4
Was meinst du nicht funktioniert? Dies ist keine sehr gute Frage - was ist passiert? Irgendwelche Fehler? Welche Fehler?
Darren Sweeney
Versuchen Sie zu lesen: stackoverflow.com/questions/18933985/…
Andrew Paramoshkin
16
Die Antwort lautet: Verwenden Sie in React keinen verschachtelten Status . Lesen Sie diese ausgezeichnete Antwort.
Qwerty
4
Was sollte stattdessen verwendet werden?
Jānis Elmeris
42
Einfach keinen verschachtelten Zustand zu verwenden, ist eine inakzeptable Antwort darauf, wie weit verbreitet React heute ist. Diese Situation wird sich ergeben und Entwickler brauchen eine Antwort darauf.
Serraosays

Antworten:

455

Um setStateein verschachteltes Objekt zu erhalten, können Sie den folgenden Ansatz verfolgen, da setState meiner Meinung nach keine verschachtelten Aktualisierungen verarbeitet.

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

Die Idee ist, ein Dummy-Objekt zu erstellen, Operationen daran auszuführen und dann den Status der Komponente durch das aktualisierte Objekt zu ersetzen

Jetzt erstellt der Spread-Operator nur eine verschachtelte Kopie des Objekts auf einer Ebene. Wenn Ihr Bundesstaat stark verschachtelt ist wie:

this.state = {
   someProperty: {
      someOtherProperty: {
          anotherProperty: {
             flag: true
          }
          ..
      }
      ...
   }
   ...
}

Sie können setState mit dem Spread-Operator auf jeder Ebene wie setzen

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

Die obige Syntax wird jedoch immer hässlicher, wenn der Status immer verschachtelter wird. Daher empfehle ich Ihnen, das immutability-helperPaket zum Aktualisieren des Status zu verwenden.

In dieser Antwort erfahren Sie, wie Sie den Status aktualisieren immutability helper.

Shubham Khatri
quelle
2
@ Ohgodwhy, nein, dies greift nicht direkt auf den Status zu, da {... this.state.someProperty} ein neues Objekt erneut bearbeitet somePropertyund wir dies
ändern
4
Das hat bei mir nicht funktioniert. Ich verwende die React-Version @ 15.6.1
Rajiv
5
@Stophface Wir können Lodash verwenden, um sicher tief zu klonen, aber nicht jeder würde diese Bibliothek einschließen, nur um State
Shubham Khatri
15
Verstößt dies nicht gegen reactjs.org/docs/… ? Wenn zwei Änderungen am verschachtelten Objekt gestapelt würden, würde die letzte Änderung die erste überschreiben. Der korrekteste Weg wäre also: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Ein bisschen langweilig ...
olejorgenb
2
Ich glaube nicht, dass Sie das ...prevState,im letzten Beispiel brauchen , Sie nur somePropertyund seine Kinder
Jemar Jones
140

Um es in einer Zeile zu schreiben

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Yoseph
quelle
12
Es wird empfohlen, den Updater für setState zu verwenden, anstatt direkt auf this.state in setState zuzugreifen. reactjs.org/docs/react-component.html#setstate
Raghu Teja
6
Ich glaube, das heißt, lesen Sie nicht this.stategleich danach setStateund erwarten Sie, dass es den neuen Wert hat. Versus Aufruf this.stateinnerhalb setState. Oder gibt es auch ein Problem mit letzterem, das mir nicht klar ist?
trpt4him
2
Wie trpt4him sagte, spricht der Link, den Sie gegeben haben, über das Problem des Zugriffs this.statenach setState. Außerdem passieren wir nicht this.statezu setState. Der Operator verteilt Eigenschaften. Es ist dasselbe wie Object.assign (). github.com/tc39/proposal-object-rest-spread
Yoseph
3
@RaghuTeja vielleicht nur machen this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });könnte erlauben, dieses Problem zu vermeiden?
Webwoman
Der Vollständigkeit halber habe ich die Antwort hinzugefügt , wie dies mit useState hook erreicht werden kann.
Andrew
90

Manchmal sind direkte Antworten nicht die besten :)

Kurzfassung:

dieser Code

this.state = {
    someProperty: {
        flag: true
    }
}

sollte als so etwas vereinfacht werden

this.state = {
    somePropertyFlag: true
}

Lange Version:

Derzeit sollten Sie nicht mit dem verschachtelten Status in React arbeiten möchten . Da React nicht darauf ausgerichtet ist, mit verschachtelten Zuständen zu arbeiten, sehen alle hier vorgeschlagenen Lösungen als Hacks aus. Sie nutzen das Framework nicht, sondern kämpfen damit. Sie schlagen vor, nicht so klaren Code zu schreiben, um einige Eigenschaften zweifelhaft zu gruppieren. Sie sind als Antwort auf die Herausforderung sehr interessant, aber praktisch nutzlos.

Stellen wir uns folgenden Zustand vor:

{
    parent: {
        child1: 'value 1',
        child2: 'value 2',
        ...
        child100: 'value 100'
    }
}

Was passiert, wenn Sie nur einen Wert von ändern child1? React rendert die Ansicht nicht erneut, da ein flacher Vergleich verwendet wird und festgestellt wird, dass sich die parentEigenschaft nicht geändert hat. Übrigens wird das direkte Mutieren des Zustandsobjekts im Allgemeinen als schlechte Praxis angesehen.

Sie müssen also das gesamte parentObjekt neu erstellen . In diesem Fall werden wir jedoch auf ein anderes Problem stoßen. React wird denken, dass alle Kinder ihre Werte geändert haben und sie alle neu rendern. Natürlich ist es nicht gut für die Leistung.

Es ist immer noch möglich, dieses Problem durch Schreiben einer komplizierten Logik zu lösen, shouldComponentUpdate()aber ich würde es vorziehen, hier anzuhalten und eine einfache Lösung aus der Kurzversion zu verwenden.

Konstantin Smolyanin
quelle
4
Ich denke, es gibt Anwendungsfälle, in denen ein verschachtelter Zustand vollkommen in Ordnung ist. ZB ein Zustand, der Schlüssel-Wert-Paare dynamisch verfolgt. Sehen Sie hier, wie Sie den Status für nur einen Eintrag im Status aktualisieren können
pors
1
Ein flacher Vergleich wird standardmäßig für reine Komponenten verwendet.
Basel Shishani
4
Ich möchte Ihnen zu Ehren einen Altar schaffen und jeden Morgen dazu beten. Ich habe drei Tage gebraucht, um zu dieser Antwort zu gelangen, die die offensichtliche Designentscheidung perfekt erklärt. Jeder versucht nur, den Spread-Operator zu verwenden oder andere Hacks durchzuführen, nur weil der verschachtelte Status besser lesbar ist.
Edeph
1
Wie schlagen Sie dann vor, mit React beispielsweise einen interaktiven Baum zu rendern (z. B. verfügt mein Unternehmen über eine Reihe von Sensoren in verschiedenen Teilen der Welt, die jeweils unterschiedliche Typen mit unterschiedlichen Eigenschaften an unterschiedlichen Standorten haben (80+) ) und natürlich sind sie in einer Hierarchie organisiert, da jede Bereitstellung Tausende von Dollar kostet und ein vollständiges Projekt ist, so dass es unmöglich wäre, sie ohne eine Hierarchie zu verwalten.) Ich bin ziemlich neugierig, wie Sie Dinge wie Bäume in React nicht als ... Bäume modellieren würden.
DanyAlejandro
10
Es scheint, dass React nicht dafür gemacht wurde, etwas darzustellen, das als rekursive Struktur von nicht trivialer Tiefe gespeichert ist. Es ist ein bisschen enttäuschend, da Baumansichten in fast jeder komplexen Anwendung angezeigt werden (Google Mail, Dateimanager, jedes Dokumentenverwaltungssystem usw.). Ich habe React für diese Dinge verwendet, hatte aber immer einen React-Fanatiker, der allen mitteilte, dass Sie Anti-Patterns ausführen, und vorschlug, dass die Lösung darin besteht, tiefe Kopien des Baums im Zustand jedes Knotens zu belassen (ja, 80 Kopien). Ich würde gerne eine nicht hackige Baumansichtskomponente sehen, die keine Reaktionsregeln verletzt.
DanyAlejandro
61

Haftungsausschluss

Verschachtelter Zustand in Reaktion ist falsches Design

Lesen Sie diese ausgezeichnete Antwort .

 

Begründung für diese Antwort:

Der setState von React ist nur ein integrierter Komfort, aber Sie werden schnell feststellen, dass er seine Grenzen hat. Durch die Verwendung benutzerdefinierter Eigenschaften und die intelligente Verwendung von forceUpdate erhalten Sie viel mehr. z.B:

class MyClass extends React.Component {
    myState = someObject
    inputValue = 42
...

MobX beispielsweise gibt den Status vollständig aus und verwendet benutzerdefinierte beobachtbare Eigenschaften.
Verwenden Sie in React-Komponenten Observables anstelle von state.

 


die Antwort auf Ihr Elend - siehe Beispiel hier

Es gibt eine andere kürzere Möglichkeit, verschachtelte Eigenschaften zu aktualisieren.

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

In einer Zeile

 this.setState(state => (state.nested.flag = false, state))

Hinweis: Dies hier ist der Komma-Operator ~ MDN . Sehen Sie ihn hier in Aktion (Sandbox) .

Es ist ähnlich (obwohl dies die Zustandsreferenz nicht ändert)

this.state.nested.flag = false
this.forceUpdate()

Für den subtilen Unterschied in diesem Zusammenhang zwischen forceUpdateund setStatesiehe das verknüpfte Beispiel.

Natürlich missbraucht dies einige Kernprinzipien, da statediese schreibgeschützt sein sollten, aber da Sie den alten Zustand sofort verwerfen und durch einen neuen Zustand ersetzen, ist dies völlig in Ordnung.

Warnung

Auch wenn die Komponente den Zustand enthält , wird aktualisiert und rerender richtig ( mit Ausnahme dieser Gotcha ) , werden die Requisiten nicht auf Kinder zu verbreiten (siehe Spymaster Kommentar unten) . Verwenden Sie diese Technik nur, wenn Sie wissen, was Sie tun.

Beispielsweise können Sie eine geänderte flache Requisite übergeben, die aktualisiert und problemlos übergeben werden kann.

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

Jetzt, obwohl sich die Referenz für complexNestedProp nicht geändert hat ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

Die Komponente wird bei jeder Aktualisierung der übergeordneten Komponente erneut gerendert, was nach dem Aufruf this.setStateoder this.forceUpdateim übergeordneten Element der Fall ist .

Auswirkungen der Mutation des Staates

Die Verwendung eines verschachtelten Status und die direkte Mutation des Status ist gefährlich, da verschiedene Objekte möglicherweise (absichtlich oder nicht) unterschiedliche (ältere) Verweise auf den Status enthalten und nicht unbedingt wissen, wann sie aktualisiert werden müssen (z. B. wenn sie verwendet werden PureComponentoder wenn shouldComponentUpdatesie für die Rückgabe implementiert sind false) ODER sind soll alte Daten wie im folgenden Beispiel anzeigen.

Stellen Sie sich eine Zeitleiste vor, die historische Daten rendern soll. Wenn Sie die Daten unter der Hand mutieren, führt dies zu unerwartetem Verhalten, da auch vorherige Elemente geändert werden.

Zustandsfluss State-Flow-verschachtelt

Wie auch immer, hier können Sie sehen, dass Nested PureChildClasses nicht neu gerendert wurde, weil Requisiten sich nicht verbreiten konnten.

Qwerty
quelle
12
Sie sollten nirgendwo einen Reaktionszustand mutieren. Wenn es verschachtelte Komponenten gibt, die Daten aus state.nested oder state.another verwenden, werden sie weder neu gerendert noch ihre untergeordneten Elemente. Dies liegt daran, dass sich das Objekt nicht geändert hat. React glaubt daher, dass sich nichts geändert hat.
Zeragamba
@ SpyMaster356 Ich habe meine Antwort aktualisiert, um dies zu beheben. Beachten Sie auch, dass MobX beispielsweise statevollständig Gräben bildet und benutzerdefinierte beobachtbare Eigenschaften verwendet. React's setStateist nur eine eingebaute Annehmlichkeit, aber Sie werden schnell feststellen, dass es seine Grenzen hat. Durch die Verwendung benutzerdefinierter Eigenschaften und die intelligente Verwendung von erhalten forceUpdateSie viel mehr. Verwenden Sie in React-Komponenten Observables anstelle von state.
Qwerty
Außerdem habe ich ein Beispiel hinzugefügt: react-experiments.herokuapp.com/state-flow (Link zu git oben)
Qwerty
Was ist, wenn Sie beispielsweise ein Objekt mit verschachtelten Attributen aus einer Datenbank in den Status laden? Wie vermeide ich in diesem Fall einen verschachtelten Zustand?
Tobiv
3
@tobiv Es ist besser, die Daten beim Abrufen in eine andere Struktur umzuwandeln, dies hängt jedoch vom Verwendungszweck ab. Es ist in Ordnung, ein verschachteltes Objekt oder ein Array von Objekten in einem Zustand zu haben, wenn Sie sie als kompakte Einheiten einiger Daten betrachten. Das Problem tritt auf, wenn Sie UI-Switches oder andere beobachtbare Daten speichern müssen (dh Daten, die die Ansicht sofort ändern sollten), da diese flach sein müssen, um interne React-Diffing-Algorithmen korrekt auszulösen. Bei Änderungen an anderen verschachtelten Objekten ist es einfach, this.forceUpdate()bestimmte Objekte auszulösen oder zu implementieren shouldComponentUpdate.
Qwerty
21

Wenn Sie ES2015 verwenden, haben Sie Zugriff auf die Object.assign. Sie können es wie folgt verwenden, um ein verschachteltes Objekt zu aktualisieren.

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

Sie führen die aktualisierten Eigenschaften mit den vorhandenen zusammen und verwenden das zurückgegebene Objekt, um den Status zu aktualisieren.

Bearbeiten: Der Zuweisungsfunktion wurde ein leeres Objekt als Ziel hinzugefügt, um sicherzustellen, dass der Status nicht direkt mutiert wird, wie Carkod hervorhob.

Alyssa Roose
quelle
12
Ich könnte mich irren, aber ich glaube nicht, dass Sie React ohne ES2015 verwenden.
Madbreaks
16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
eyecandy.dev
quelle
1
scheint hier die elegantere Lösung zu sein: 1) Vermeidung der mühsamen Ausbreitung in setState und 2) Setzen Sie den neuen Status in setState gut ein, ohne den direkt in setState zu mutieren. Last but not least 3) Vermeiden Sie die Verwendung einer Bibliothek für nur eine einfache Aufgabe
Webwoman
Die beiden Beispiele sind nicht gleichwertig. Wenn das propertymehrere verschachtelte Eigenschaften enthält, löscht die erste diese, die zweite nicht. Codesandbox.io/s/nameless-pine-42thu
Qwerty
Qwerty, Sie haben Recht, danke für die Mühe. Ich habe die ES6-Version entfernt.
eyecandy.dev
Einfach und unkompliziert!
Tony Sepia
Ich habe deine Lösung benutzt. Mein Staatsobjekt ist klein und es hat gut funktioniert. Wird React nur für den geänderten Status gerendert oder wird alles gerendert?
Kenji Noguchi
14

Es gibt viele Bibliotheken, die dabei helfen. Zum Beispiel mit Unveränderlichkeitshelfer :

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

Mit lodash / fp set:

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

Verwenden von lodash / fp merge:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});
tokland
quelle
Wenn Sie verwenden lodash, möchten Sie wahrscheinlich versuchen _.cloneDeep, den Status als geklonte Kopie festzulegen.
Yixing Liu
@YixingLiu: Ich habe die zu verwendende Antwort aktualisiert lodash/fp, damit wir uns keine Gedanken über Mutationen machen müssen.
tokland
Gibt es einen Vorteil für die Auswahl mergeoder setFunktionen in lodash / fp neben der Syntax?
Toivo Säwén
Gute Antwort, vielleicht möchten Sie hinzufügen, dass Sie auch this.setState(newState)die lodash / fp-Methoden aufrufen müssen. Ein weiteres Problem ist, dass lodash .set(non-fp) eine andere Reihenfolge von Argumenten hat, die mich für eine Weile gestolpert haben.
Toivo Säwén
7

Wir verwenden Immer https://github.com/mweststrate/immer , um diese Art von Problemen zu behandeln.

Ersetzen Sie einfach diesen Code in einer unserer Komponenten

this.setState(prevState => ({
   ...prevState,
        preferences: {
            ...prevState.preferences,
            [key]: newValue
        }
}));

Mit diesem

import produce from 'immer';

this.setState(produce(draft => {
    draft.preferences[key] = newValue;
}));

Mit immer behandeln Sie Ihren Zustand als "normales Objekt". Die Magie geschieht hinter den Kulissen mit Proxy-Objekten.

Joakim Jäderberg
quelle
6

Hier ist eine Variation der ersten Antwort in diesem Thread, für die keine zusätzlichen Pakete, Bibliotheken oder Sonderfunktionen erforderlich sind.

state = {
  someProperty: {
    flag: 'string'
  }
}

handleChange = (value) => {
  const newState = {...this.state.someProperty, flag: value}
  this.setState({ someProperty: newState })
}

Um den Status eines bestimmten verschachtelten Felds festzulegen, haben Sie das gesamte Objekt festgelegt. Dazu habe ich eine Variable erstellt newStateund den Inhalt des aktuellen Status zuerst mit dem Spreizoperator ES2015 in ihn verteilt . Dann habe ich den Wert von this.state.flagdurch den neuen Wert ersetzt (da ich festgelegt habe, flag: value nachdem ich den aktuellen Status in das Objekt verteilt habe, wird das flagFeld im aktuellen Status überschrieben). Dann setze ich einfach den Status somePropertyauf mein newStateObjekt.

Matthew Berkompas
quelle
6

Obwohl das Verschachteln nicht wirklich so ist, wie Sie einen Komponentenzustand behandeln sollten, ist es manchmal einfach, das Verschachteln in einer Ebene zu vereinfachen.

Für einen Staat wie diesen

state = {
 contact: {
  phone: '888-888-8888',
  email: '[email protected]'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

Eine wiederverwendbare Methode würde so aussehen.

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

Geben Sie dann einfach den obj-Namen für jede Verschachtelung ein, die Sie adressieren möchten ...

<TextField
 name="street"
 onChange={handleChange('address')}
 />
user2208124
quelle
4

Ich habe diese Lösung verwendet.

Wenn Sie einen verschachtelten Status wie diesen haben:

   this.state = {
          formInputs:{
            friendName:{
              value:'',
              isValid:false,
              errorMsg:''
            },
            friendEmail:{
              value:'',
              isValid:false,
              errorMsg:''
            }
}

Sie können die handleChange-Funktion deklarieren, die den aktuellen Status kopiert und ihn mit geänderten Werten neu zuweist

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let statusCopy = Object.assign({}, this.state);
    statusCopy.formInputs[inputName].value = inputValue;

    this.setState(statusCopy);
  }

hier das html mit dem event listener

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
quelle
Bestätigt. Dies funktioniert hervorragend, wenn Sie bereits mit einem bereits eingerichteten Projekt mit verschachtelten Eigenschaften arbeiten.
metal_jacke1
4

Erstellen Sie eine Kopie des Status:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

Nehmen Sie Änderungen an diesem Objekt vor:

someProperty.flag = "false"

Aktualisieren Sie jetzt den Status

this.setState({someProperty})
Dhakad
quelle
setState ist asynchron. Während der Aktualisierung kann eine andere Person diese Funktion aufrufen und eine veraltete Version des Status kopieren.
Aviv Ben Shabat
3

Obwohl Sie nach dem Status einer klassenbasierten React-Komponente gefragt haben, besteht das gleiche Problem mit dem useState-Hook. Noch schlimmer: Der useState-Hook akzeptiert keine Teilaktualisierungen. Daher wurde diese Frage sehr relevant, als useState hook eingeführt wurde.

Ich habe beschlossen, die folgende Antwort zu veröffentlichen, um sicherzustellen, dass die Frage modernere Szenarien abdeckt, in denen der useState-Hook verwendet wird:

Wenn Sie haben:

const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

Sie können die verschachtelte Eigenschaft festlegen, indem Sie die aktuelle Eigenschaft klonen und die erforderlichen Datensegmente patchen. Beispiel:

setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });

Oder Sie können die Immer-Bibliothek verwenden, um das Klonen und Patchen des Objekts zu vereinfachen.

Oder Sie können die Hookstate-Bibliothek (Haftungsausschluss: Ich bin Autor) verwenden, um einfach komplexe (lokale und globale) Statusdaten vollständig zu verwalten und die Leistung zu verbessern (lesen Sie: Keine Sorge um die Renderoptimierung):

import { useStateLink } from '@hookstate/core' 
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

Holen Sie sich das Feld zu rendern:

state.nested.someProperty.nested.flag.get()
// or 
state.get().someProperty.flag

Legen Sie das verschachtelte Feld fest:

state.nested.someProperty.nested.flag.set(false)

Hier ist das Hookstate-Beispiel, bei dem der Status tief / rekursiv in einer baumartigen Datenstruktur verschachtelt ist .

Andrew
quelle
2

Zwei weitere Optionen, die noch nicht erwähnt wurden:

  1. Wenn Sie einen tief verschachtelten Status haben, prüfen Sie, ob Sie die untergeordneten Objekte so umstrukturieren können, dass sie an der Wurzel sitzen. Dies erleichtert die Aktualisierung der Daten.
  2. In den Redux-Dokumenten sind viele praktische Bibliotheken für den Umgang mit unveränderlichen Zuständen verfügbar . Ich empfehle Immer, da es Ihnen ermöglicht, Code auf mutative Weise zu schreiben, aber das notwendige Klonen hinter den Kulissen übernimmt. Außerdem wird das resultierende Objekt eingefroren, sodass Sie es später nicht versehentlich mutieren können.
Cory House
quelle
2

Um die Dinge allgemeiner zu gestalten, habe ich an den Antworten von @ ShubhamKhatri und @ Qwerty gearbeitet.

Zustandsobjekt

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

Eingabesteuerung

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

updateState-Methode

setState als Antwort von @ ShubhamKhatri

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState als Antwort von @ Qwerty

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

Hinweis: Diese oben genannten Methoden funktionieren nicht für Arrays

Venugopal
quelle
2

Ich nehme die bereits geäußerten Bedenken hinsichtlich der Erstellung einer vollständigen Kopie Ihres Komponentenstatus sehr ernst . Vor diesem Hintergrund würde ich Immer dringend empfehlen .

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

Dies sollte funktionieren React.PureComponent(dh flache Zustandsvergleiche durch React), da Immerein Proxy-Objekt geschickt verwendet wird, um einen beliebig tiefen Zustandsbaum effizient zu kopieren. Immer ist außerdem typsicherer als Bibliotheken wie Immutability Helper und ideal für Benutzer von Javascript und Typescript.


Typescript- Dienstprogrammfunktion

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
Stephen Paul
quelle
1
stateUpdate = () => {
    let obj = this.state;
    if(this.props.v12_data.values.email) {
      obj.obj_v12.Customer.EmailAddress = this.props.v12_data.values.email
    }
    this.setState(obj)
}
user3444748
quelle
1
Ich denke nicht, dass dies richtig ist, da objes sich nur um einen Hinweis auf den Zustand handelt. Dadurch mutieren Sie das eigentliche this.stateObjekt
Ciprian Tomoiagă
0

Ich fand, dass dies für mich funktioniert, da ich in meinem Fall ein Projektformular habe, in dem Sie beispielsweise eine ID und einen Namen haben, und ich lieber den Status für ein verschachteltes Projekt beibehalten möchte.

return (
  <div>
      <h2>Project Details</h2>
      <form>
        <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
        <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
      </form> 
  </div>
)

Gib mir Bescheid!

Michael Stokes
quelle
0

So etwas könnte ausreichen,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 
Eladian
quelle
0

Ich weiß, dass es eine alte Frage ist, wollte aber trotzdem mitteilen, wie ich dies erreicht habe. Angenommen, der Status im Konstruktor sieht folgendermaßen aus:

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

    this.handleChange = this.handleChange.bind(this);
  }

Meine handleChangeFunktion ist wie folgt:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

Und stellen Sie sicher, dass Sie die Eingaben entsprechend benennen:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>
Elnoor
quelle
0

Ich mache verschachtelte Updates mit einer reduzierten Suche:

Beispiel:

Die verschachtelten Variablen im Status:

state = {
    coords: {
        x: 0,
        y: 0,
        z: 0
    }
}

Die Funktion:

handleChange = nestedAttr => event => {
  const { target: { value } } = event;
  const attrs = nestedAttr.split('.');

  let stateVar = this.state[attrs[0]];
  if(attrs.length>1)
    attrs.reduce((a,b,index,arr)=>{
      if(index==arr.length-1)
        a[b] = value;
      else if(a[b]!=null)
        return a[b]
      else
        return a;
    },stateVar);
  else
    stateVar = value;

  this.setState({[attrs[0]]: stateVar})
}

Verwenden:

<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>
Emisael Carrera
quelle
0

Dies ist mein Anfangszustand

    const initialStateInput = {
        cabeceraFamilia: {
            familia: '',
            direccion: '',
            telefonos: '',
            email: ''
        },
        motivoConsulta: '',
        fechaHora: '',
        corresponsables: [],
    }

Der Hook oder Sie können ihn durch den Status (Klassenkomponente) ersetzen.

const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);

Die Methode für handleChange

const actualizarState = e => {
    const nameObjects = e.target.name.split('.');
    const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
    setInfoAgendamiento({...newState});
};

Methode zum Festlegen des Status mit verschachtelten Status

const setStateNested = (state, nameObjects, value) => {
    let i = 0;
    let operativeState = state;
    if(nameObjects.length > 1){
        for (i = 0; i < nameObjects.length - 1; i++) {
            operativeState = operativeState[nameObjects[i]];
        }
    }
    operativeState[nameObjects[i]] = value;
    return state;
}

Schließlich ist dies die Eingabe, die ich verwende

<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />
Ramiro Carbonell Delgado
quelle
0

Wenn Sie formik in Ihrem Projekt verwenden, gibt es eine einfache Möglichkeit, mit diesen Dingen umzugehen. Hier ist der einfachste Weg, um mit formik umzugehen.

Legen Sie zuerst Ihre Anfangswerte im Attribut formik initivalues ​​oder in der Reaktion fest. Zustand

Hier werden die Anfangswerte im Reaktionszustand definiert

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

Definieren Sie die obigen Anfangswerte für das formik-Feld innerhalb des formik- initiValuesAttributs

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

Erstellen Sie eine Konsole, um den Status zu überprüfen, in den stringanstelle booleander formik- setFieldValueFunktion aktualisiert wurde, um den Status festzulegen , oder verwenden Sie das React- Debugger-Tool, um die Änderungen in den formik-Statuswerten anzuzeigen.

Sharad Sharma
quelle
0

Versuchen Sie diesen Code:

this.setState({ someProperty: {flag: false} });
Soumya
quelle
somePropertyflag
Dadurch
0

Ich sah folgendes in einem Buch:

this.setState(state => state.someProperty.falg = false);

aber ich bin nicht sicher, ob es richtig ist ..

Dewy Eve
quelle