Untuk memodifikasi objek / variabel yang bersarang dalam kondisi React, biasanya tiga metode digunakan: JavaScript vanilla Object.assign
, immutability-helper dan cloneDeep
dari Lodash .
Ada juga banyak lib pihak ketiga lain yang kurang populer untuk mencapai ini, tetapi dalam jawaban ini, saya akan membahas hanya tiga opsi ini. Juga, beberapa metode JavaScript vanilla tambahan ada, seperti penyebaran array, (lihat jawaban @ mpen misalnya), tetapi mereka tidak terlalu intuitif, mudah digunakan dan mampu menangani semua situasi manipulasi negara.
Seperti yang ditunjukkan berkali-kali dalam komentar-komentar teratas atas jawaban, yang penulisnya mengusulkan mutasi langsung negara: jangan lakukan itu . Ini adalah anti-pola Bereaksi di mana-mana, yang pasti akan mengakibatkan konsekuensi yang tidak diinginkan. Pelajari cara yang benar.
Mari kita bandingkan tiga metode yang banyak digunakan.
Diberikan struktur objek keadaan ini:
state = {
outer: {
inner: 'initial value'
}
}
Anda dapat menggunakan metode berikut untuk memperbarui nilai inner
bidang paling dalam tanpa mempengaruhi bagian lain negara.
1. Object.assign JavaScript milik Vanilla
const App = () => {
const [outer, setOuter] = React.useState({ inner: 'initial value' })
React.useEffect(() => {
console.log('Before the shallow copying:', outer.inner) // initial value
const newOuter = Object.assign({}, outer, { inner: 'updated value' })
console.log('After the shallow copy is taken, the value in the state is still:', outer.inner) // initial value
setOuter(newOuter)
}, [])
console.log('In render:', outer.inner)
return (
<section>Inner property: <i>{outer.inner}</i></section>
)
}
ReactDOM.render(
<App />,
document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<main id="react"></main>
Ingat, Object.assign itu itu tidak akan melakukan kloning yang dalam , karena hanya menyalin nilai properti , dan itulah sebabnya apa yang dilakukannya disebut penyalinan dangkal (lihat komentar).
Agar ini berfungsi, kita hanya boleh memanipulasi properti tipe primitif ( outer.inner
), yaitu string, angka, boolean.
Dalam contoh ini, kami membuat konstanta baru ( const newOuter...
), menggunakan Object.assign
, yang membuat objek kosong ( {}
), salinanouter
objek ( { inner: 'initial value' }
) ke dalamnya dan kemudian menyalin objek yang berbeda { inner: 'updated value' }
di atasnya.
Dengan cara ini, pada akhirnya baru dibuat newOuter
konstanta yang akan menyimpan nilai { inner: 'updated value' }
sejak inner
properti ditimpa. Ini newOuter
adalah objek baru, yang tidak ditautkan ke objek dalam keadaan, sehingga dapat dimutasi sesuai kebutuhan dan negara akan tetap sama dan tidak berubah sampai perintah untuk memperbaruinya dijalankan.
Bagian terakhir adalah menggunakan setOuter()
setter untuk menggantikan yang asli outer
di negara bagian dengan objek yang baru dibuat newOuter
(hanya nilainya yang akan berubah, nama properti outer
tidak akan).
Sekarang bayangkan kita memiliki keadaan yang lebih dalam seperti state = { outer: { inner: { innerMost: 'initial value' } } }
. Kita bisa mencoba membuat newOuter
objek dan mengisinya dengan outer
konten dari negara, tetapiObject.assign
tidak akan dapat menyalin innerMost
nilai ke objek yang baru dibuat ini newOuter
karena innerMost
bersarang terlalu dalam.
Anda masih bisa menyalin inner
, seperti pada contoh di atas, tetapi karena sekarang objek dan bukan primitif, referensi darinewOuter.inner
akan disalin ke outer.inner
sebaliknya, yang berarti bahwa kita akan berakhir dengan newOuter
objek lokal yang secara langsung terkait dengan objek di negara bagian .
Itu berarti bahwa dalam hal ini mutasi yang dibuat secara lokal newOuter.inner
akan secara langsung mempengaruhi outer.inner
objek (dalam keadaan), karena mereka sebenarnya menjadi hal yang sama (dalam memori komputer).
Object.assign
karena itu hanya akan berfungsi jika Anda memiliki struktur keadaan dalam satu tingkat yang relatif sederhana dengan anggota terdalam yang memegang nilai dari tipe primitif.
Jika Anda memiliki objek yang lebih dalam (tingkat 2 atau lebih), yang harus Anda perbarui, jangan gunakan Object.assign
. Anda berisiko mengalami mutasi secara langsung.
2. KlonDeep Lodash
const App = () => {
const [outer, setOuter] = React.useState({ inner: 'initial value' })
React.useEffect(() => {
console.log('Before the deep cloning:', outer.inner) // initial value
const newOuter = _.cloneDeep(outer) // cloneDeep() is coming from the Lodash lib
newOuter.inner = 'updated value'
console.log('After the deeply cloned object is modified, the value in the state is still:', outer.inner) // initial value
setOuter(newOuter)
}, [])
console.log('In render:', outer.inner)
return (
<section>Inner property: <i>{outer.inner}</i></section>
)
}
ReactDOM.render(
<App />,
document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<main id="react"></main>
CloneDeep Lodash jauh lebih mudah digunakan. Ini melakukan kloning yang mendalam , jadi ini adalah opsi yang kuat, jika Anda memiliki keadaan yang cukup kompleks dengan objek multi-level atau array di dalamnya. HanyacloneDeep()
properti negara tingkat atas, mutasikan bagian yang dikloning dengan cara apa pun yang Anda suka, dan setOuter()
kembali ke negara bagian.
3. penolong kekal
const App = () => {
const [outer, setOuter] = React.useState({ inner: 'initial value' })
React.useEffect(() => {
const update = immutabilityHelper
console.log('Before the deep cloning and updating:', outer.inner) // initial value
const newOuter = update(outer, { inner: { $set: 'updated value' } })
console.log('After the cloning and updating, the value in the state is still:', outer.inner) // initial value
setOuter(newOuter)
}, [])
console.log('In render:', outer.inner)
return (
<section>Inner property: <i>{outer.inner}</i></section>
)
}
ReactDOM.render(
<App />,
document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://wzrd.in/standalone/immutability-helper@3.0.0"></script>
<main id="react"></main>
immutability-helper
membawanya ke tingkat yang baru, dan hal yang keren tentang hal itu adalah bahwa hal itu tidak hanya dapat $set
nilai untuk item negara, tetapi juga $push
, $splice
, $merge
(dll) mereka. Berikut adalah daftar perintah tersedia.
Catatan samping
Sekali lagi, perlu diingat, itu setOuter
hanya memodifikasi properti tingkat pertama objek negara ( outer
dalam contoh ini), bukan yang bersarang secara mendalam (outer.inner
). Jika berperilaku dengan cara yang berbeda, pertanyaan ini tidak akan ada.
Mana yang tepat untuk proyek Anda?
Jika Anda tidak ingin atau tidak dapat menggunakan dependensi eksternal , dan memiliki struktur keadaan yang sederhana , patuhi Object.assign
.
Jika Anda memanipulasi kondisi besar dan / atau rumit , Lodash'scloneDeep
adalah pilihan yang bijaksana.
Jika Anda memerlukan kemampuan tingkat lanjut , yaitu jika struktur negara Anda kompleks dan Anda perlu melakukan semua jenis operasi di atasnya, cobalahimmutability-helper
, itu adalah alat yang sangat canggih yang dapat digunakan untuk manipulasi negara.
... atau, apakah Anda benar - benar perlu melakukan ini sama sekali?
Jika Anda menyimpan data yang kompleks dalam status React, mungkin ini saat yang tepat untuk memikirkan cara lain untuk menanganinya. Mengatur objek keadaan kompleks tepat di komponen Bereaksi bukanlah operasi langsung, dan saya sangat menyarankan untuk memikirkan pendekatan yang berbeda.
Kemungkinan besar Anda lebih baik menyimpan data kompleks Anda di toko Redux, mengaturnya di sana menggunakan reduksi dan / atau kisah-kisah dan mengaksesnya menggunakan penyeleksi.