I'm mencoba untuk mengatur negara dengan menggunakan nested properti seperti ini:
this.state = {
someProperty: {
flag:true
}
}
Tapi memperbarui keadaan seperti ini,
this.setState({ someProperty.flag: false });
doesn't bekerja. Bagaimana hal ini dapat dilakukan dengan benar?
Dalam rangka untuk setState
untuk bersarang objek yang dapat anda ikuti di bawah ini pendekatan seperti yang saya pikir setState doesn't menangani bersarang update.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
Idenya adalah untuk membuat dummy objek melakukan operasi di atasnya dan kemudian mengganti komponen's negara dengan diperbarui objek
Sekarang, penyebaran operator menciptakan hanya satu tingkat bersarang salinan objek. Jika negara anda sangat bersarang seperti:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
Anda bisa setState menggunakan menyebar operator pada setiap tingkat seperti
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
Namun sintaks di atas mendapatkan setiap jelek seperti negara menjadi lebih dan lebih bersarang dan oleh karena itu saya menyarankan anda untuk menggunakan ketetapan-helper
paket untuk memperbarui negara.
Melihat ini jawaban tentang bagaimana untuk memperbarui negara dengan ketetapan helper
.
Kadang-kadang langsung jawaban tidak yang terbaik :)
Versi pendek:
kode ini
this.state = {
someProperty: {
flag: true
}
}
harus disederhanakan sebagai sesuatu seperti
this.state = {
somePropertyFlag: true
}
Versi panjang:
Saat ini seharusnya kau't ingin bekerja dengan nested negara dalam Bereaksi. Karena Bereaksi tidak berorientasi untuk bekerja dengan nested serikat dan semua solusi yang diusulkan di sini terlihat sebagai hacks. Mereka don't menggunakan framework tapi berjuang dengan itu. Mereka menyarankan untuk menulis tidak begitu jelas kode untuk diragukan tujuan dari pengelompokan beberapa sifat. Jadi mereka sangat menarik sebagai jawaban atas tantangan tetapi praktis tidak berguna.
Mari kita bayangkan negara berikut:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
Apa yang akan terjadi jika anda mengubah nilai child1
? Bereaksi tidak akan kembali membuat pandangan karena menggunakan dangkal perbandingan dan itu akan menemukan bahwa orang tua
properti didn't perubahan. BTW bermutasi keadaan objek langsung ini dianggap sebagai praktek yang buruk secara umum.
Jadi, anda perlu membuat ulang seluruh orang tua
objek. Tapi dalam kasus ini kita akan bertemu dengan masalah lain. Bereaksi akan berpikir bahwa semua anak-anak telah mengubah nilai-nilai mereka dan akan membuat kembali semua dari mereka. Tentu saja hal ini tidak baik untuk kinerja.
Hal ini masih mungkin untuk memecahkan masalah itu dengan menulis beberapa logika rumit di shouldComponentUpdate()
tapi aku akan lebih memilih untuk berhenti di sini dan menggunakan solusi sederhana dari versi pendek.
Bersarang Negara dalam Bereaksi adalah salah desain
Baca sangat baik ini jawaban.
Alasan di balik jawaban ini:
Bereaksi's setState adalah built-in berkualitas, tetapi anda akan segera menyadari yang memiliki batas-batasnya. Menggunakan custom properties dan penggunaan cerdas forceUpdate memberi anda jauh lebih banyak. misalnya:
class MyClass meluas Bereaksi.Komponen { myState = someObject inputValue = 42 ...
MobX, misalnya, parit negara sepenuhnya dan menggunakan custom sifat yang dapat diamati. Gunakan Diamati bukan negara dalam Bereaksi komponen.
Ada yang lain pendek cara untuk update apapun bersarang properti.
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
ini.setState(negara => (negara.bersarang.flag = false, negara bagian))
Ini adalah secara efektif yang sama sebagai
this.state.nested.flag = false
this.forceUpdate()
Untuk perbedaan yang halus dalam konteks ini antara forceUpdate
dan setState
melihat terkait contoh.
Tentu saja ini adalah menyalahgunakan beberapa prinsip-prinsip inti, sebagai negara
harus baca-saja, tapi karena anda segera membuang negara yang lama dan menggantinya dengan negara baru, itu benar-benar ok.
Meskipun mengandung komponen negara will update dan rerender benar (kecuali ini gotcha), alat peraga akan fail untuk menyebarkan untuk anak-anak (lihat kepala mata-mata's komentar di bawah ini). Hanya menggunakan teknik ini jika anda tahu apa yang anda lakukan.
Misalnya, anda mungkin melewati berubah datar prop yang diperbarui dan lulus dengan mudah.
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Sekarang bahkan meskipun referensi untuk complexNestedProp tidak berubah (shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
komponen will rerender setiap kali orangtua komponen update, yang terjadi setelah memanggil ini.setState
atau ini.forceUpdate
di orang tua.
Menggunakan bersarang negara dan bermutasi negara secara langsung berbahaya karena benda-benda yang berbeda mungkin memegang (sengaja atau tidak) yang berbeda (lebih tua) referensi negara dan mungkin tidak selalu tahu kapan harus update (misalnya ketika menggunakan PureComponent
atau jika shouldComponentUpdate
ini dilaksanakan untuk kembali false
) ATAU dimaksudkan untuk menampilkan data lama seperti pada contoh di bawah ini.
Bayangkan, waktu yang seharusnya untuk membuat bersejarah data, merubah data di bawah tangan akan mengakibatkan perilaku yang tidak diharapkan seperti itu juga akan mengubah item sebelumnya.
Pokoknya di sini anda dapat melihat bahwa Bersarang PureChildClass
itu tidak rerendered karena alat peraga gagal untuk menyebarkan.
Jika anda menggunakan ES2015 anda memiliki akses ke Objek.tetapkan. Anda dapat menggunakan it sebagai berikut untuk memperbarui objek bersarang.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
Anda menggabungkan sifat diperbarui dengan yang ada dan menggunakan kembali objek untuk memperbarui negara.
Edit: Menambahkan objek kosong sebagai target untuk menetapkan fungsi untuk memastikan negara isn't bermutasi secara langsung sebagai carkod menunjuk keluar.
Ada banyak library untuk membantu dengan ini. Misalnya, menggunakan ketetapan-pembantu:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Menggunakan lodash/fp set:
import {set} from 'lodash/fp';
const newState = set(["someProperty", "flag"], false, this.state);
Menggunakan lodash/fp penggabungan:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
Berikut ini's variasi pada jawaban pertama yang diberikan di thread ini yang doesn't membutuhkan paket-paket tambahan, perpustakaan atau fungsi-fungsi khusus.
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
Dalam rangka untuk mengatur keadaan tertentu bersarang di lapangan, anda telah mengatur seluruh objek. Saya melakukan ini dengan membuat sebuah variabel, newState
dan menyebarkan isi dari keadaan saat ini ke dalamnya first menggunakan ES2015 menyebar operator. Kemudian, saya menggantikan nilai ini.negara.bendera
dengan nilai baru (karena saya set bendera: nilai
after saya menyebar keadaan saat ini menjadi objek, bendera
lapangan dalam keadaan saat ditimpa). Maka, saya hanya mengatur negara someProperty
saya newState
objek.
Kami menggunakan Immer https://github.com/mweststrate/immer untuk menangani jenis-jenis masalah.
Hanya diganti kode ini di salah satu dari komponen
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
Dengan ini
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
Dengan immer anda menangani negara sebagai "normal objek". Keajaiban terjadi di belakang layar dengan proxy benda-benda.
Meskipun bersarang isn't benar-benar bagaimana anda harus memperlakukan komponen negara, kadang-kadang untuk sesuatu yang mudah untuk single tier bersarang.
Untuk keadaan seperti ini
state = {
contact: {
phone: '888-888-8888',
email: '[email protected]'
}
address: {
street:''
},
occupation: {
}
}
Re-useable metode ive digunakan akan terlihat seperti ini.
handleChange = (obj) => e => {
let x = this.state[obj];
x[e.target.name] = e.target.value;
this.setState({ [obj]: x });
};
kemudian hanya lewat di obj nama untuk masing-masing bersarang anda inginkan ke alamat...
<TextField
name="street"
onChange={handleChange('address')}
/>
Saya menggunakan solusi ini.
Jika anda memiliki bersarang negara seperti ini:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
anda dapat mendeklarasikan handleChange fungsi yang salin status dan kembali memberikan itu dengan mengubah nilai-nilai
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 acara pendengar
<input type="text" onChange={this.handleChange} " name="friendName" />
Membuat copy dari negara:
mari someProperty = JSON.mengurai(JSON.stringify(ini.negara.someProperty))
membuat perubahan pada objek ini:
someProperty.flag = "salah"
sekarang update negara
ini.setState({someProperty})
Untuk membuat hal-hal yang generik, saya bekerja di @ShubhamKhatri's dan @Qwerty's jawaban.
keadaan objek
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
masukan kontrol
<input
value={this.state.name}
onChange={this.updateState}
type="text"
name="name"
/>
<input
value={this.state.grandParent.parent1.child}
onChange={this.updateState}
type="text"
name="grandParent.parent1.child"
/>
<input
value={this.state.grandParent.parent2.child}
onChange={this.updateState}
type="text"
name="grandParent.parent2.child"
/>
updateState metode
setState sebagai @ShubhamKhatri's jawaban
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const oldstate = this.state;
const newstate = { ...oldstate };
let newStateLevel = newstate;
let oldStateLevel = oldstate;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
newStateLevel[path[i]] = event.target.value;
} else {
newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
oldStateLevel = oldStateLevel[path[i]];
newStateLevel = newStateLevel[path[i]];
}
}
this.setState(newstate);
}
setState sebagai @Qwerty's jawaban
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const state = { ...this.state };
let ref = state;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
ref[path[i]] = event.target.value;
} else {
ref = ref[path[i]];
}
}
this.setState(state);
}
Catatan: Ini metode di atas tidak't bekerja untuk array
Dua pilihan lain yang tidak disebutkan belum:
Saya mengambil sangat serius yang menyangkut sudah disuarakan sekitar menciptakan salinan lengkap dari komponen negara. Dengan mengatakan bahwa, saya akan sangat menyarankan Immer.
import produce from 'immer';
<input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
Ini harus bekerja untuk Bereaksi.PureComponent
(yaitu dangkal negara perbandingan dengan Bereaksi) sebagai Immer
cerdik menggunakan proxy objek yang efisien untuk menyalin sewenang-wenang dalam keadaan pohon. Immer juga lebih typesafe dibandingkan dengan perpustakaan seperti Ketetapan Penolong, dan sangat ideal untuk Javascript dan Ketangkasan pengguna yang sama.
Naskah fungsi utilitas
function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s:
Draft<Readonly<S>>) => any) {
comp.setState(produce(comp.state, s => { fn(s); }))
}
onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
stateUpdate = () => { mari obj = ini.negara; jika(hal ini.alat peraga.v12_data.nilai-nilai.email) { obj.obj_v12.Pelanggan.EmailAddress = ini.alat peraga.v12_data.nilai-nilai.email } ini.setState(obj) }
Jika anda menggunakan formik dalam proyek anda memiliki beberapa cara mudah untuk menangani hal ini. Berikut ini adalah cara yang paling mudah untuk dilakukan dengan formik.
Pertama mengatur nilai awal dalam formik initivalues atribut atau dalam bereaksi. negara
Di sini, nilai awal adalah menentukan bereaksi negara
state = {
data: {
fy: {
active: "N"
}
}
}
tentukan di atas initialValues untuk formik dalam bidang formik initiValues
atribut
<Formik
initialValues={this.state.data}
onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
<Form>
<Field type="checkbox" name="fy.active" onChange={(e) => {
const value = e.target.checked;
if(value) setFieldValue('fy.active', 'Y')
else setFieldValue('fy.active', 'N')
}}/>
</Form>
)}
</Formik>
Membuat konsol untuk memeriksa keadaan diperbarui ke string
, bukan boolean ' tidak formik
setFieldValue` fungsi untuk mengatur negara atau pergi dengan bereaksi debugger tool untuk melihat perubahan dalam formik nilai-nilai negara.
Saya menemukan ini bekerja untuk saya, memiliki bentuk proyek dalam kasus saya di mana misalnya anda memiliki id, dan nama dan I'd agak mempertahankan negara untuk bersarang proyek.
return (
<div>
<h2>Project Details</h2>
<form>
<input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
<input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
</form>
</div>
)
Biarkan aku tahu!
Sesuatu seperti ini mungkin cukup,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
Aku tahu itu adalah sebuah pertanyaan lama tapi masih ingin berbagi bagaimana saya mencapai ini. Dengan asumsi negara dalam constructor akan terlihat seperti ini:
constructor(props) {
super(props);
this.state = {
loading: false,
user: {
email: ""
},
organization: {
name: ""
}
};
this.handleChange = this.handleChange.bind(this);
}
Saya handleChange
fungsi seperti ini:
handleChange(e) {
const names = e.target.name.split(".");
const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
this.setState((state) => {
state[names[0]][names[1]] = value;
return {[names[0]]: state[names[0]]};
});
}
Dan pastikan anda masukan nama sesuai:
<input
type="text"
name="user.email"
onChange={this.handleChange}
value={this.state.user.firstName}
placeholder="Email Address"
/>
<input
type="text"
name="organization.name"
onChange={this.handleChange}
value={this.state.organization.name}
placeholder="Organization Name"
/>