Memperbarui objek dengan setState di Bereaksi

265

Apakah mungkin untuk memperbarui properti objek dengan setState?

Sesuatu seperti:

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

Saya telah mencoba:

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

dan ini:

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

Hasil pertama dalam kesalahan sintaks dan yang kedua tidak melakukan apa-apa. Ada ide?

JohnSnow
sumber
5
kode kedua akan bekerja namun Anda akan kehilangan ageproperti di dalamnya jasper.
giorgim

Jawaban:

578

Ada beberapa cara untuk melakukan ini, karena pembaruan negara adalah operasi async , jadi untuk memperbarui objek keadaan, kita perlu menggunakan fungsi pembaru dengan setState.

1- yang paling sederhana:

Pertama buat salinan jasperlalu lakukan perubahan itu:

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

Alih-alih menggunakan Object.assignkami juga dapat menulis seperti ini:

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

2- Menggunakan operator 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
    }
}))

Catatan: Object.assign dan Spread Operatorhanya membuat salinan dangkal , jadi jika Anda telah mendefinisikan objek bersarang atau array objek, Anda memerlukan pendekatan yang berbeda.


Memperbarui objek keadaan tersarang:

Asumsikan Anda telah mendefinisikan status sebagai:

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

Untuk memperbarui objek pizza ekstraCheese:

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

Memperbarui berbagai objek:

Mari kita asumsikan Anda memiliki aplikasi todo, dan Anda mengelola data dalam formulir ini:

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

Untuk memperbarui status objek todo apa pun, jalankan peta pada larik dan periksa beberapa nilai unik dari setiap objek, jika ada condition=true, kembalikan objek baru dengan nilai yang diperbarui, atau objek yang sama.

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

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

}))

Saran: Jika objek tidak memiliki nilai unik, maka gunakan indeks array.

Mayank Shukla
sumber
Cara saya mencoba sekarang bekerja ... cara kedua: S
JohnSnow
7
@ JohnSnow dalam Anda kedua itu akan menghapus properti lain dari jasperobjek, apakah console.log(jasper)Anda hanya akan melihat satu kunci name, usia tidak akan ada :)
Mayank Shukla
1
@ JohnSnow, ya cara ini wajar jika jaspermerupakan object, jangan yang terbaik atau tidak, mungkin solusi lain yang lebih baik adalah mungkin :)
Mayank Shukla
6
Baik untuk disebutkan di sini bahwa tidak ada Object.assignoperator yang juga menyalin properti yang dalam Dengan cara ini Anda harus menggunakan solusi seperti lodash deepCopydll.
Alex Vasilev
1
Bagaimana kalau let { jasper } = this.state?
Brian Le
37

Ini adalah cara tercepat dan paling mudah dibaca:

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

Bahkan jika this.state.jaspersudah mengandung properti nama, nama baru name: 'someothername'dengan yang digunakan.

mannok
sumber
38
Solusi ini memiliki satu kelemahan besar: untuk mengoptimalkan pembaruan status Bereaksi mungkin mengelompokkan beberapa pembaruan. Sebagai konsekuensinya this.state.jaspertidak harus mengandung keadaan terbaru. Lebih baik gunakan notasi dengan prevState.
berdosa
33

Gunakan operator spread dan beberapa ES6 di sini

this.setState({
    jasper: {
          ...this.state.jasper,
          name: 'something'
    }
})
Nikhil
sumber
Ini memperbarui jasper tetapi properti lainnya dihapus dari negara.
iaq
11

Saya menggunakan solusi ini.

Jika Anda memiliki status bersarang seperti ini:

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

Anda dapat mendeklarasikan fungsi handleChange yang menyalin status saat ini dan menetapkan kembali dengan nilai yang diubah

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

di sini html dengan pendengar acara. Pastikan untuk menggunakan nama yang sama yang digunakan untuk objek negara (dalam hal ini 'friendName')

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
sumber
Ini bekerja untuk saya, kecuali saya harus menggunakan ini sebagai gantinya:statusCopy.formInputs[inputName] = inputValue;
MikeyE
9

Saya tahu ada banyak jawaban di sini, tapi saya terkejut tidak satupun dari mereka membuat salinan objek baru di luar setState, dan kemudian cukup setState ({newObject}). Bersih, ringkas, dan andal. Jadi dalam hal ini:

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

Atau untuk properti dinamis (sangat berguna untuk formulir)

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

Colemerrick
sumber
7

coba ini, itu akan berfungsi dengan baik

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));
Burak Kahraman
sumber
4
Anda bermutasi objek negara secara langsung. Untuk menggunakan pendekatan ini, tambahkan objek baru sebagai sumber:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia
3

Kasus pertama memang merupakan kesalahan sintaksis.

Karena saya tidak dapat melihat sisa komponen Anda, sulit untuk melihat mengapa Anda bersarang objek di negara Anda di sini. Itu bukan ide yang baik untuk bersarang objek dalam keadaan komponen. Coba atur status awal Anda menjadi:

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

Dengan begitu, jika Anda ingin memperbarui nama, Anda bisa menghubungi:

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

Apakah itu akan mencapai apa yang Anda tuju?

Untuk penyimpanan data yang lebih besar dan lebih kompleks, saya akan menggunakan sesuatu seperti Redux. Tapi itu jauh lebih maju.

Aturan umum dengan status komponen adalah menggunakannya hanya untuk mengelola keadaan UI komponen (misalnya aktif, timer, dll.)

Lihatlah referensi ini:

mccambridge
sumber
1
Saya hanya menggunakan itu sebagai contoh, objek harus bersarang .. Saya mungkin harus menggunakan Redux tapi saya mencoba memahami React fundementals ..
JohnSnow
2
Ya, saya akan tinggal jauh dari Redux untuk saat ini. Saat Anda mempelajari dasar-dasarnya, cobalah untuk menjaga data Anda tetap sederhana. Itu akan membantu Anda menghindari kasus aneh yang membuat Anda tersandung. 👍
mccambridge
2

Pilihan lain: tentukan variabel Anda dari objek Jasper dan kemudian panggil variabel.

Operator penyebaran: 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
sumber
2

Cara sederhana dan dinamis.

Ini akan melakukan pekerjaan, tetapi Anda harus mengatur semua id ke induk sehingga orang tua akan menunjuk ke nama objek, menjadi id = "jasper" dan nama nama elemen input = properti di dalam objek jasper .

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });
Alejandro Sanchez Duran
sumber
Saya setuju dengan baris pertama, karena ini adalah satu-satunya hal yang berfungsi untuk objek saya di negara! Juga dihapus di mana saya terjebak dalam memperbarui data saya. Sejauh ini cara yang paling sederhana dan dinamis ... Terima kasih !!
Smit
2

Anda dapat mencoba ini : ( Catatan : nama tag input === bidang objek)

<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
sumber
Selamat datang di Stack Overflow. Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana kode ini menjawab pertanyaan meningkatkan nilai jangka panjangnya. Bagaimana Menjawab
Elletlar
2

ini adalah solusi lain menggunakan utilitas imutabe immer , sangat cocok untuk objek bersarang dengan mudah, dan Anda tidak perlu peduli tentang mutasi

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername
    })
)
perang yang sama
sumber
1

Juga, mengikuti solusi Alberto Piras, jika Anda tidak ingin menyalin semua objek "state":

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
sumber
1

Anda dapat mencoba ini:

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

atau untuk properti lainnya:

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

Atau Anda dapat menggunakan fungsi 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}
    })
}

dan kode 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}
/>
Nemanja Stojanovic
sumber
Ini berfungsi tanpa JSON.stringify .. this.setState (prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; return {document: prevState}}); ..Dimana dokumen adalah objek jenis negara ..
Oleg Borodko
1

Tanpa menggunakan Async dan Tunggu ...

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

Jika Anda menggunakan dengan Async Dan Tunggu gunakan ini ...

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

Pengaturan ini berhasil bagi saya:

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

this.setState({newState: newState});

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