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.