Aktualisieren eines Objekts mit setState in React

265

Ist es überhaupt möglich, die Objekteigenschaften mit zu aktualisieren setState?

Etwas wie:

this.state = {
   jasper: { name: 'jasper', age: 28 },
}

Ich habe versucht:

this.setState({jasper.name: 'someOtherName'});

und das:

this.setState({jasper: {name: 'someothername'}})

Der erste führt zu einem Syntaxfehler und der zweite führt einfach zu nichts. Irgendwelche Ideen?

JohnSnow
quelle
5
Der zweite Code hätte funktioniert, aber Sie hätten die ageEigenschaft im Inneren verloren jasper.
Giorgim

Antworten:

573

Es gibt mehrere Möglichkeiten, dies zu tun, da die Statusaktualisierung eine asynchrone Operation ist. Um das Statusobjekt zu aktualisieren, müssen wir die Updater-Funktion mit verwenden setState.

1- Einfachste:

Erstellen Sie zuerst eine Kopie von und nehmen Sie jasperdann die folgenden Änderungen vor:

this.setState(prevState => {
  let jasper = Object.assign({}, prevState.jasper);  // creating copy of state variable jasper
  jasper.name = 'someothername';                     // update the name property, assign a new value                 
  return { jasper };                                 // return new object jasper object
})

Anstatt zu verwenden Object.assign, können wir es auch so schreiben:

let jasper = { ...prevState.jasper };

2- Verwenden des Spread-Operators :

this.setState(prevState => ({
    jasper: {                   // object that we want to update
        ...prevState.jasper,    // keep all other key-value pairs
        name: 'something'       // update the value of specific key
    }
}))

Hinweis: Object.assign und Spread Operatorerstellt nur eine flache Kopie . Wenn Sie also ein verschachteltes Objekt oder ein Array von Objekten definiert haben, benötigen Sie einen anderen Ansatz.


Verschachteltes Statusobjekt aktualisieren:

Angenommen, Sie haben den Status wie folgt definiert:

this.state = {
  food: {
    sandwich: {
      capsicum: true,
      crackers: true,
      mayonnaise: true
    },
    pizza: {
      jalapeno: true,
      extraCheese: false
    }
  }
}

So aktualisieren Sie extraCheese des Pizzaobjekts:

this.setState(prevState => ({
  food: {
    ...prevState.food,           // copy all other key-value pairs of food object
    pizza: {                     // specific object of food object
      ...prevState.food.pizza,   // copy all pizza key-value pairs
      extraCheese: true          // update value of specific key
    }
  }
}))

Array von Objekten aktualisieren:

Nehmen wir an, Sie haben eine ToDo-App und verwalten die Daten in folgender Form:

this.state = {
  todoItems: [
    {
      name: 'Learn React Basics',
      status: 'pending'
    }, {
      name: 'Check Codebase',
      status: 'pending'
    }
  ]
}

Um den Status eines Aufgabenobjekts zu aktualisieren, führen Sie eine Zuordnung auf dem Array aus und suchen Sie nach einem eindeutigen Wert für jedes Objekt. Geben Sie im Falle von condition=truedas neue Objekt mit dem aktualisierten Wert zurück, andernfalls dasselbe Objekt.

let key = 2;
this.setState(prevState => ({

  todoItems: prevState.todoItems.map(
    el => el.key === key? { ...el, status: 'done' }: el
  )

}))

Vorschlag: Wenn das Objekt keinen eindeutigen Wert hat, verwenden Sie den Array-Index.

Mayank Shukla
quelle
Die Art und Weise, wie ich es jetzt versucht habe, funktioniert jedoch ... die zweite Art: S
JohnSnow
7
@ JohnSnow in Ihrer Sekunde wird es die anderen Eigenschaften aus dem jasperObjekt entfernen , tun console.log(jasper)Sie, dass Sie nur einen Schlüssel sehen name, Alter wird nicht da sein :)
Mayank Shukla
1
@ JohnSnow, ja, dieser Weg ist richtig, wenn jasperes ein ist object, nicht das Beste oder nicht, vielleicht sind andere bessere Lösungen möglich :)
Mayank Shukla
6
Gut zu erwähnen, dass weder Object.assignOperator Deep Copy-Eigenschaften noch verbreiten. Auf diese Weise sollten Sie Problemumgehungen wie lodash deepCopyusw. verwenden
Alex Vasilev
1
Wie wäre es let { jasper } = this.state?
Brian Le
37

Dies ist der schnellste und am besten lesbare Weg:

this.setState({...this.state.jasper, name: 'someothername'});

Auch wenn this.state.jasperbereits eine Namenseigenschaft enthalten ist, wird der neue Name name: 'someothername'verwendet.

Mannok
quelle
38
Diese Lösung hat einen großen Nachteil: Um Statusaktualisierungen zu optimieren, kann React mehrere Aktualisierungen gruppieren. Infolgedessen this.state.jasperenthält nicht unbedingt den neuesten Stand. Verwenden Sie besser die Notation mit dem prevState.
gesündigt
33

Verwenden Sie hier den Spread-Operator und etwas ES6

this.setState({
    jasper: {
          ...this.state.jasper,
          name: 'something'
    }
})
Nikhil
quelle
Dadurch wird Jasper aktualisiert, andere Eigenschaften werden jedoch aus dem Status entfernt.
iaq
11

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 Funktion handleChange 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. Stellen Sie sicher, dass Sie denselben Namen verwenden, der auch für das Statusobjekt verwendet wird (in diesem Fall 'friendName').

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
quelle
Dies funktionierte für mich, außer dass ich dies stattdessen verwenden musste:statusCopy.formInputs[inputName] = inputValue;
MikeyE
9

Ich weiß, dass es hier viele Antworten gibt, aber ich bin überrascht, dass keine von ihnen eine Kopie des neuen Objekts außerhalb von setState erstellt und dann einfach setState ({newObject}). Sauber, prägnant und zuverlässig. Also in diesem Fall:

const jasper = { ...this.state.jasper, name: 'someothername' }
this.setState(() => ({ jasper }))

Oder für eine dynamische Eigenschaft (sehr nützlich für Formulare)

const jasper = { ...this.state.jasper, [VarRepresentingPropertyName]: 'new value' }
this.setState(() => ({ jasper }))

Colemerrick
quelle
7

Versuchen Sie dies, es sollte gut funktionieren

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));
Burak Kahraman
quelle
4
Sie mutieren das Statusobjekt direkt. Um diesen Ansatz zu verwenden, fügen Sie ein neues Objekt als Quelle hinzu:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia
3

Der erste Fall ist in der Tat ein Syntaxfehler.

Da ich den Rest Ihrer Komponente nicht sehen kann, ist es schwer zu verstehen, warum Sie hier Objekte in Ihrem Status verschachteln. Es ist keine gute Idee, Objekte im Komponentenstatus zu verschachteln. Versuchen Sie, Ihren Ausgangszustand wie folgt einzustellen:

this.state = {
  name: 'jasper',
  age: 28
}

Auf diese Weise können Sie, wenn Sie den Namen aktualisieren möchten, einfach Folgendes aufrufen:

this.setState({
  name: 'Sean'
});

Wird das das erreichen, was Sie anstreben?

Für größere, komplexere Datenspeicher würde ich so etwas wie Redux verwenden. Aber das ist viel weiter fortgeschritten.

Die allgemeine Regel für den Komponentenstatus lautet, ihn nur zum Verwalten des Benutzeroberflächenstatus der Komponente zu verwenden (z. B. aktiv, Timer usw.).

Schauen Sie sich diese Referenzen an:

McCambridge
quelle
1
Ich habe das nur als Beispiel verwendet, das Objekt muss verschachtelt sein. Ich sollte wahrscheinlich Redux verwenden, aber ich versuche, die Grundlagen von React zu verstehen.
JohnSnow
2
Ja, ich würde mich vorerst von Redux fernhalten. Versuchen Sie, Ihre Daten einfach zu halten, während Sie die Grundlagen lernen. Das hilft Ihnen, seltsame Fälle zu vermeiden, die Sie stören. 👍
McCambridge
2

Eine weitere Option: Definieren Sie Ihre Variable aus dem Jasper- Objekt heraus und rufen Sie dann einfach eine Variable auf.

Spread-Operator: ES6

this.state = {  jasper: { name: 'jasper', age: 28 } } 

let foo = "something that needs to be saved into state" 

this.setState(prevState => ({
    jasper: {
        ...jasper.entity,
        foo
    }
})
Luis Martins
quelle
2

Einfache und dynamische Art und Weise.

Dies erledigt den Job, aber Sie müssen alle IDs auf das übergeordnete Element setzen, damit das übergeordnete Element auf den Namen des Objekts zeigt, dh id = "jasper", und den Namen der Eigenschaft input element = innerhalb des Objektjasper benennt .

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });
Alejandro Sanchez Duran
quelle
Ich stimme der ersten Zeile zu, da dies das einzige ist, was für mein Objekt von Objekten im Zustand funktioniert hat! Außerdem wurde klar, wo ich bei der Aktualisierung meiner Daten feststeckte. Mit Abstand der einfachste und dynamischste Weg ... Danke !!
Smit
2

Sie können Folgendes versuchen : ( Hinweis : Name des Eingabe-Tags === Feld des Objekts)

<input name="myField" type="text" 
      value={this.state.myObject.myField} 
     onChange={this.handleChangeInpForm}>
</input>

-----------------------------------------------------------
handleChangeInpForm = (e) => {
   let newObject = this.state.myObject;
   newObject[e.target.name] = e.target.value;
   this.setState({
     myObject: newObject 
   })
}
Xuanduong Ngo
quelle
Willkommen bei Stack Overflow. Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert. Wie zu
antworten
2

Dies ist eine weitere Lösung, die das Dienstprogramm immer immutabe verwendet und sich sehr gut für tief verschachtelte Objekte eignet. Sie sollten sich nicht um Mutationen kümmern

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername
    })
)
Samehanwar
quelle
1

Wenn Sie nach der Alberto Piras-Lösung nicht das gesamte "Status" -Objekt kopieren möchten:

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

    let jasperCopy = Object.assign({}, this.state.jasper);
    jasperCopy[inputName].name = inputValue;

    this.setState({jasper: jasperCopy});
  }
Marc LaQuay
quelle
1

Sie können dies versuchen:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.name = 'someOtherName';
   return {jasper: prevState}
})

oder für andere Eigenschaft:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.age = 'someOtherAge';
   return {jasper: prevState}
})

Oder Sie können die handleChage-Funktion verwenden:

handleChage(event) {
   const {name, value} = event.target;
    this.setState(prevState => {
       prevState = JSON.parse(JSON.stringify(this.state.jasper));
       prevState[name] = value;
       return {jasper: prevState}
    })
}

und HTML-Code:

<input 
   type={"text"} 
   name={"name"} 
   value={this.state.jasper.name} 
   onChange={this.handleChange}
/>
<br/>
<input 
   type={"text"} 
   name={"age"} 
   value={this.state.jasper.age} 
   onChange={this.handleChange}
/>
Nemanja Stojanovic
quelle
Dies funktioniert ohne JSON.stringify .. this.setState (prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; return {document: prevState}}); ..Wo Dokument ist Zustandstyp Objekt ..
Oleg Borodko
1

Ohne Async und Await zu verwenden Verwenden Sie diese ...

funCall(){    
     this.setState({...this.state.jasper, name: 'someothername'});
}

Wenn Sie mit Async And Await verwenden, verwenden Sie diese ...

async funCall(){
      await this.setState({...this.state.jasper, name: 'someothername'});
}
Shantanu Sharma
quelle
0

Dieses Setup hat bei mir funktioniert:

let newState = this.state.jasper;
newState.name = 'someOtherName';

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
LaZza
quelle