Aggiornamento di un oggetto con setState in React


265

È possibile aggiornare le proprietà dell'oggetto con setState?

Qualcosa di simile a:

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

Ho provato:

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

e questo:

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

Il primo provoca un errore di sintassi e il secondo non fa nulla. Qualche idea?


5
il secondo codice avrebbe funzionato comunque avresti perso la ageproprietà all'interno jasper.
giorgim,

Risposte:


579

Esistono diversi modi per farlo, poiché l'aggiornamento dello stato è un'operazione asincrona , quindi per aggiornare l'oggetto stato, è necessario utilizzare la funzione di aggiornamento con setState.

1- Quello più semplice:

Prima crea una copia e jasperpoi fai le modifiche in questo:

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
})

Invece di usare Object.assignpossiamo anche scrivere in questo modo:

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

2- Utilizzo dell'operatore spread :

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
    }
}))

Nota: Object.assign e Spread Operatorcrea solo copie superficiali , quindi se hai definito un oggetto nidificato o una matrice di oggetti, hai bisogno di un approccio diverso.


Aggiornamento oggetto stato nidificato:

Supponiamo di aver definito lo stato come:

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

Per aggiornare extraCheese of pizza object:

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
    }
  }
}))

Aggiornamento array di oggetti:

Supponiamo che tu abbia un'app todo e che tu gestisca i dati in questo modulo:

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

Per aggiornare lo stato di qualsiasi oggetto todo, eseguire una mappa sull'array e verificare la presenza di un valore univoco per ogni oggetto, in caso di condition=true, restituire il nuovo oggetto con valore aggiornato, altrimenti lo stesso oggetto.

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

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

}))

Suggerimento: se l'oggetto non ha un valore univoco, utilizzare l'indice di array.


Il modo in cui stavo provando ora funziona però ... il secondo modo: S
JohnSnow,

7
@JohnSnow in te secondo rimuoverà le altre proprietà jasperdall'oggetto, console.log(jasper)vedrai solo una chiave name, l'età non ci sarà :)
Mayank Shukla

1
@JohnSnow, sì, in questo modo è corretto se jasperè un object, non è il migliore o no, potrebbero essere possibili altre soluzioni migliori :)
Mayank Shukla,

6
Buono a menzionare qui che né Object.assignné diffondere le proprietà di copia profonda dell'operatore. In questo modo dovresti usare soluzioni alternative come lodash deepCopyecc.
Alex Vasilev,

1
Che ne dici let { jasper } = this.state?
Brian Le,

37

Questo è il modo più veloce e più leggibile:

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

Anche se this.state.jaspercontiene già una proprietà name, utilizzare il nuovo nome name: 'someothername'.


38
Questa soluzione presenta un grosso svantaggio: al fine di ottimizzare gli aggiornamenti di stato, React potrebbe raggruppare più aggiornamenti. Di conseguenza this.state.jaspernon contiene necessariamente l'ultimo stato. Meglio usare la notazione con il prevState.
peccato l'

33

Utilizzare l'operatore di diffusione e alcuni ES6 qui

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

Questo aggiorna il diaspro ma altre proprietà vengono rimosse dallo stato.
iaq

11

Ho usato questa soluzione.

Se hai uno stato nidificato come questo:

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

è possibile dichiarare la funzione handleChange che copia lo stato corrente e lo riassegna con i valori modificati

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);
  }

qui l'html con il listener di eventi. Assicurati di usare lo stesso nome usato nell'oggetto state (in questo caso 'friendName')

<input type="text" onChange={this.handleChange} " name="friendName" />

Questo ha funzionato per me, tranne che ho dovuto usare questo invece:statusCopy.formInputs[inputName] = inputValue;
MikeyE

9

So che ci sono molte risposte qui, ma sono sorpreso che nessuno di loro crei una copia del nuovo oggetto al di fuori di setState, e quindi semplicemente setState ({newObject}). Pulito, conciso e affidabile. Quindi in questo caso:

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

O per una proprietà dinamica (molto utile per i moduli)

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


7

prova questo, dovrebbe funzionare bene

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));

4
Stai mutando direttamente l'oggetto stato. Per utilizzare questo approccio, aggiungi un nuovo oggetto come fonte:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia

3

Il primo caso è in effetti un errore di sintassi.

Dal momento che non riesco a vedere il resto del tuo componente, è difficile capire perché stai annidando oggetti nel tuo stato qui. Non è una buona idea nidificare oggetti nello stato componente. Prova a impostare lo stato iniziale su:

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

In questo modo, se vuoi aggiornare il nome, puoi semplicemente chiamare:

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

Riuscirà a raggiungere ciò a cui stai mirando?

Per archivi di dati più grandi e complessi, utilizzerei qualcosa come Redux. Ma è molto più avanzato.

La regola generale con lo stato del componente è di usarlo solo per gestire lo stato dell'interfaccia utente del componente (ad es. Attivo, timer, ecc.)

Dai un'occhiata a questi riferimenti:


1
L'ho usato solo come esempio, l'oggetto deve essere nidificato .. Probabilmente dovrei usare Redux ma sto cercando di capire i fondamenti di React ..
JohnSnow,

2
Sì, per ora starei lontano da Redux. Mentre stai imparando i fondamenti, prova a mantenere semplici i tuoi dati. Ciò ti aiuterà a evitare casi strani che ti inciampano. 👍
mccambridge,

2

Un'altra opzione: definisci la tua variabile dall'oggetto Jasper e poi chiama semplicemente una variabile.

Operatore di diffusione: 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
    }
})

2

Modo semplice e dinamico.

Questo farà il lavoro, ma devi impostare tutti gli id ​​sul genitore in modo che il genitore indicherà il nome dell'oggetto, essendo id = "jasper" e nominerà il nome dell'elemento input = proprietà all'interno dell'oggetto jasper .

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });

Sono d'accordo con la prima riga, poiché questa è l'unica cosa che ha funzionato per il mio oggetto di oggetti in stato! Inoltre ha chiarito dove ero bloccato nell'aggiornamento dei miei dati. Di gran lunga il modo più semplice e dinamico ... Grazie !!
Smit

2

Puoi provare con questo : ( Nota : nome del tag di input === campo dell'oggetto)

<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 
   })
}

Benvenuto in Stack Overflow. Mentre questo codice può rispondere alla domanda, fornendo un contesto aggiuntivo riguardo al perché e / o al modo in cui questo codice risponde alla domanda migliora il suo valore a lungo termine. Come rispondere
Elletlar,

2

questa è un'altra soluzione che utilizza l' utilità immerabe immer , molto adatta per oggetti profondamente annidati con facilità e non dovresti preoccuparti della mutazione

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

1

Inoltre, seguendo la soluzione di Alberto Piras, se non si desidera copiare tutto l'oggetto "stato":

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});
  }

1

Puoi provare con questo:

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

o per altre proprietà:

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

Oppure puoi usare la funzione handleChage:

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

e codice HTML:

<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}
/>

Funziona senza JSON.stringify .. this.setState (prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; return {document: prevState}}); ..Dove documento è un oggetto di tipo stato ..
Oleg Borodko,

1

Senza usare Async e Await Usa questo ...

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

Se usi con Async And Await usa questo ...

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

0

Questa configurazione ha funzionato per me:

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

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.