Wie kann ich state.item aktualisieren[1] im Zustand mit setState?

Lesezeit: 9 Minuten

Benutzer-Avatar
Martin

Ich erstelle eine App, in der der Benutzer sein eigenes Formular entwerfen kann. Geben Sie zB den Namen des Feldes und Details darüber an, welche anderen Spalten 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, auf das richtige Objekt abzuzielen:

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 um es zu aktualisieren items[1].name ?

Benutzer-Avatar
mpen

Da in diesem Thread viele Fehlinformationen enthalten sind, können Sie dies ohne Hilfsbibliotheken tun:

handleChange: function (e) {
    // 1. Make a shallow copy of the items
    let items = [...this.state.items];
    // 2. Make a shallow copy of the item you want to mutate
    let item = {...items[1]};
    // 3. Replace the property you're intested in
    item.name="newName";
    // 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
    items[1] = item;
    // 5. Set the state to our new copy
    this.setState({items});
},

Sie können die Schritte 2 und 3 kombinieren, wenn Sie möchten:

let item = {
    ...items[1],
    name: 'newName'
}

Oder Sie können das Ganze in einer Zeile erledigen:

this.setState(({items}) => ({
    items: [
        ...items.slice(0,1),
        {
            ...items[1],
            name: 'newName',
        },
        ...items.slice(2)
    ]
}));

Hinweis: Ich habe gemacht items eine Anordnung. OP hat ein Objekt verwendet. Die Konzepte sind jedoch die gleichen.


Sie können sehen, was in Ihrem Terminal / Ihrer Konsole vor sich geht:

❯ node
> items = [{name:'foo'},{name:'bar'},{name:'baz'}]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> clone = [...items]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> item1 = {...clone[1]}
{ name: 'bar' }
> item1.name="bacon"
'bacon'
> clone[1] = item1
{ name: 'bacon' }
> clone
[ { name: 'foo' }, { name: 'bacon' }, { name: 'baz' } ]
> items
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] // good! we didn't mutate `items`
> items === clone
false // these are different objects
> items[0] === clone[0]
true // we don't need to clone items 0 and 2 because we're not mutating them (efficiency gains!)
> items[1] === clone[1]
false // this guy we copied

  • @TranslucentCloud Oh ja, die Hilfsmethoden sind auf jeden Fall nett, aber ich denke, jeder sollte wissen, was unter der Haube vor sich geht 🙂

    – mp

    5. April 2018 um 20:01 Uhr

  • Warum brauchen wir Schritt 2 und 3? Warum können wir den Klon nicht direkt nach dem ersten Schritt mutieren?

    – Jewmorow

    5. Dezember 2018 um 6:09 Uhr

  • @Evmorov Weil es kein tiefer Klon ist. Schritt 1 ist nur das Klonen des Arrays, nicht der Objekte darin. Mit anderen Worten, jedes der Objekte innerhalb des neuen Arrays „zeigt“ immer noch auf die vorhandenen Objekte im Speicher – das Mutieren eines mutiert das andere (sie sind ein und dasselbe). Siehe auch die items[0] === clone[0] bit in meinem Terminal-Beispiel unten. Verdreifachen = überprüft, ob die Objekte auf dasselbe verweisen.

    – mp

    5. Dezember 2018 um 22:12 Uhr


  • @IanWarburton Nicht wirklich. items.findIndex() sollte damit kurzen Prozess machen.

    – mp

    10. Juni 2019 um 21:58 Uhr


  • Vielen Dank. Ich hatte Mühe, ein Zustandselement eines Arrays zu aktualisieren. Du hast mir viel Zeit gespart.

    – Watanabe.N

    19. April 2021 um 13:40 Uhr

Benutzer-Avatar
Jonny Buchanan

Du könntest die verwenden update Unveränderlichkeitshelfer dafür:

this.setState({
  items: update(this.state.items, {1: {name: {$set: 'updated field name'}}})
})

Oder wenn es Ihnen egal ist, Änderungen an diesem Element in a shouldComponentUpdate() Lebenszyklusmethode mit ===könnten Sie den Zustand direkt bearbeiten und die Komponente zum erneuten Rendern zwingen – dies ist praktisch die gleiche wie die Antwort von @limelights, da ein Objekt aus dem Zustand gezogen und bearbeitet wird.

this.state.items[1].name="updated field name"
this.forceUpdate()

Post-Edit-Ergänzung:

Probier das aus Einfache Komponentenkommunikation Lektion aus Reaktionstraining ein Beispiel dafür, wie eine Callback-Funktion von einer übergeordneten Komponente mit Status an eine untergeordnete Komponente übergeben wird, die eine Statusänderung auslösen muss.

  • Wenn Sie diese Antwort lesen: stackoverflow.com/a/51018315/2396744, benötigen Sie meiner Meinung nach immer noch die Updater-Form der setstate-Funktion, die den Prevstate-Callback verwendet.

    – ist gegangen

    16. Oktober 2020 um 12:52 Uhr

  • Vielen Dank, Jonny, einen Tag verschwendet und endlich eine Lösung bekommen.

    – Himanshu-Padia

    5. März 2021 um 6:34 Uhr

Benutzer-Avatar
MarvinVK

Falscher Weg!

handleChange = (e) => {
    const { items } = this.state;
    items[1].name = e.target.value;

    // update state
    this.setState({
        items,
    });
};

Wie von vielen besseren Entwicklern in den Kommentaren hervorgehoben: den Staat zu mutieren ist falsch!

Ich habe eine Weile gebraucht, um das herauszufinden. Oben funktioniert, aber es nimmt die Kraft von React. Zum Beispiel componentDidUpdate wird dies nicht als Update sehen, da es direkt geändert wird.

So der richtige Weg wäre:

handleChange = (e) => {
    this.setState(prevState => ({
        items: {
            ...prevState.items,
            [prevState.items[1].name]: e.target.value,
        },
    }));
};

  • ES6, nur weil Sie “const” verwenden?

    – nkolaw

    21. Juli 2016 um 15:34 Uhr

  • Ruft nicht an items[1].role = e.target.value Zustand direkt mutieren?

    – Antonius

    20. März 2017 um 17:33 Uhr

  • Sie mutieren den Zustand, das ist völlig gegen die Idee, den Zustand unveränderlich zu halten, wie die vorgeschlagene Reaktion. Dies kann Ihnen bei einer großen Anwendung viel Schmerz bereiten.

    – ncubica

    21. April 2017 um 1:26 Uhr

  • @MarvinVK, Ihre Antwort lautet “Best Practice wäre also:” gefolgt von der Verwendung von “this.forceUpdate();” was nicht empfohlen wird, da es durch setState() überschrieben werden könnte, siehe facebook.github.io/react/docs/react-component.html#state. Ändern Sie dies am besten, damit es für zukünftige Leser nicht verwirrend ist.

    – Jakob Z.

    1. August 2017 um 7:27 Uhr

  • Ich wollte nur darauf hinweisen, dass viele der Kommentare jetzt aufgrund von Änderungen und Änderungen irrelevant sind der richtige Weg ist eigentlich der richtige Weg.

    – hihi

    13. Januar 2019 um 15:42 Uhr

Benutzer-Avatar
Jonas

Ich hatte das gleiche Problem. Hier ist eine einfache Lösung, die funktioniert!

const newItems = [...this.state.items];
newItems[item] = value;
this.setState({ items:newItems });

  • Object.assign führt keine tiefe Kopie durch. Sprich, wenn a = {c: {d: 1}} und b = Object.assign({}, a)dann führen Sie aus b.c.d = 4dann a.c.d ist mutiert.

    – Ohl

    7. November 2017 um 20:06 Uhr

  • Du hast recht, der Wert 1 des innersten Objekts (a.c.d) wird mutiert. Aber wenn Sie den Nachfolger der ersten Ebene neu zuweisen bso was: b.c = {f: 1}der entsprechende Teil von a wird nicht mutiert (es wird bleiben {d: 1}). Trotzdem netter Fang, ich werde die Antwort sofort aktualisieren.

    – Neurotransmitter

    7. November 2017 um 20:23 Uhr


  • Was Sie definiert haben, ist eigentlich a shallow copy und nicht ein deep copy. Es ist leicht zu verwechseln, was shallow copy meint. Im shallow copy, a !== baber für jeden Schlüssel aus dem Quellobjekt a, a[key] === b[key]

    – Ohl

    8. November 2017 um 12:26 Uhr

  • Ja, ausdrücklich erwähnte Oberflächlichkeit Object.assign in der Antwort.

    – Neurotransmitter

    20. Februar 2018 um 11:44 Uhr

  • JSON.parse(JSON.stringify(object)) ist auch eine Variante für Deep Clone. Die Leistung ist schlechter als bei Lodash cloneDeep obwohl. measurethat.net/Benchmarks/Show/2751/0/…

    – Tylik

    24. April 2019 um 10:44 Uhr


Benutzer-Avatar
eicksl

Laut der React-Dokumentation auf setStateverwenden Object.assign wie andere Antworten hier vermuten lassen, ist nicht ideal. Aufgrund der Natur von setStateAufgrund des asynchronen Verhaltens von , können nachfolgende Aufrufe, die diese Technik verwenden, frühere Aufrufe außer Kraft setzen, was zu unerwünschten Ergebnissen führt.

Stattdessen empfehlen die React-Dokumente, die Updater-Form von zu verwenden setState die auf dem vorherigen Zustand arbeitet. Denken Sie daran, wenn Sie ein Array oder Objekt aktualisieren Sie müssen ein neues Array oder Objekt zurückgeben da React von uns verlangt, die Unveränderlichkeit des Zustands zu bewahren. Wenn Sie den Spread-Operator der ES6-Syntax zum flachen Kopieren eines Arrays verwenden, würde das Erstellen oder Aktualisieren einer Eigenschaft eines Objekts an einem bestimmten Index des Arrays wie folgt aussehen:

this.setState(prevState => {
    const newItems = [...prevState.items];
    newItems[index].name = newName;
    return {items: newItems};
})

  • Dies ist eine angemessene Antwort, wenn Sie ES6 verwenden. Es ist inline, was @Jonas geantwortet hat. Aufgrund der Erklärung fällt es jedoch auf.

    – Surabh

    13. Juni 2019 um 14:19 Uhr

  • Ja, dieser Code funktioniert einwandfrei und ist sehr einfach.

    – Shoeb Mirza

    7. Oktober 2019 um 10:24 Uhr

Benutzer-Avatar
Henrik Andersson

Holen Sie sich zuerst das gewünschte Element, ändern Sie, was Sie an diesem Objekt wünschen, 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.

handleChange: function (e) {
   item = this.state.items[1];
   item.name="newName";
   items[1] = item;

   this.setState({items: items});
}

  • Dies ist eine angemessene Antwort, wenn Sie ES6 verwenden. Es ist inline, was @Jonas geantwortet hat. Aufgrund der Erklärung fällt es jedoch auf.

    – Surabh

    13. Juni 2019 um 14:19 Uhr

  • Ja, dieser Code funktioniert einwandfrei und ist sehr einfach.

    – Shoeb Mirza

    7. Oktober 2019 um 10:24 Uhr

Benutzer-Avatar
kabirbaidhya

Verändern Sie nicht den Status an Ort und Stelle. Dies kann zu unerwarteten Ergebnissen führen. Ich habe meine Lektion gelernt! Arbeiten Sie immer mit einer Kopie/einem Klon, Object.assign() ist gut:

item = Object.assign({}, this.state.items[1], {name: 'newName'});
items[1] = item;
this.setState({items: items});

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

  • was ist items in deinem beispiel? Meinten Sie this.state.items oder etwas anderes?

    – Buh Buh

    6. Februar 2018 um 11:27 Uhr

  • Ich habe das getestet, aber ihm fehlt eine Zeile. Über items[1] = item; Es sollte eine Zeile mit der Aufschrift geben items = this.state.items;. Passen Sie auf, mein Javascript ist eingerostet und ich lerne, für mein Heimprojekt zu reagieren, also habe ich keine Ahnung, ob das gut oder schlecht ist 🙂

    – Greg0ry

    6. Oktober 2019 um 16:20 Uhr

1019580cookie-checkWie kann ich state.item aktualisieren[1] im Zustand mit setState?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy