Vanilya JavaScript'in: tipik olarak üç yöntem kullanılır durumuna, tepki derin iç içe nesneler / değişkenleri değiştirmek için Object.assign
, değişmezlik-yardımcı ve cloneDeep
gelen Lodash .
Bunu başarmak için daha az popüler üçüncü taraf kütüphaneleri de var, ancak bu cevapta sadece bu üç seçeneği ele alacağım. Ayrıca, dizi yayma gibi bazı ek vanilya JavaScript yöntemleri de mevcuttur (örneğin @ mpen'in cevabına bakın), ancak çok sezgisel değil, kullanımı kolay ve tüm durum manipülasyon durumlarını idare edebilir.
As üst sivri sayısız kez yazarlar devletin doğrudan mutasyon teklif cevapları, açıklama olarak vardı: sadece bunu yapmaz . Bu, kaçınılmaz olarak istenmeyen sonuçlara yol açacak olan her yerde bulunan bir Tepki anti-paternidir. Doğru yolu öğrenin.
Yaygın olarak kullanılan üç yöntemi karşılaştıralım.
Bu durum nesne yapısı göz önüne alındığında:
state = {
outer: {
inner: 'initial value'
}
}
inner
Eyaletin geri kalanını etkilemeden en içteki alanın değerini güncellemek için aşağıdaki yöntemleri kullanabilirsiniz .
1. Vanilya JavaScript'in Object.assign
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>
O, unutmayın Object.assign derin klonlama gerçekleştirmez , çünkü sadece kopyalar gayrimenkul değerleri ve en o yüzden ona bir denir ne kopyalayarak sığ (yorumlar).
Bunun çalışması için sadece ilkel tiplerin ( outer.inner
), yani dizelerin, sayıların, booleanların özelliklerini manipüle etmeliyiz .
Bu örnekte, yeni bir sabit (oluştururken const newOuter...
kullanarak) Object.assign
(boş bir nesne oluşturur ki, {}
), kopyalar outer
(nesne { inner: 'initial value' }
içine) ve daha sonra kopya farklı bir nesne { inner: 'updated value' }
üzerinde o.
Bu şekilde, yeni oluşturulan newOuter
sabit { inner: 'updated value' }
, inner
mülkün geçersiz kılınmasından bu yana bir değer tutacaktır . Bu newOuter
, durumdaki nesneyle bağlantılı olmayan yepyeni bir nesnedir, bu nedenle gerektiğinde mutasyona uğrayabilir ve durum, güncelleme komutu verilinceye kadar aynı kalır ve değişmez.
Son bölüm, durumdaki setOuter()
orijinali outer
yeni oluşturulan bir newOuter
nesneyle değiştirmek için ayarlayıcıyı kullanmaktır (yalnızca değer değişecektir, özellik adı değişmeyecektir outer
).
Şimdi daha derin bir durumumuz olduğunu hayal edin state = { outer: { inner: { innerMost: 'initial value' } } }
. newOuter
Nesneyi oluşturmaya ve durumun outer
içeriğiyle doldurmaya Object.assign
çalışabiliriz , ancak çok derin iç içe olduğundan , innerMost
değerini bu yeni oluşturulan newOuter
nesneye kopyalayamayacağız innerMost
.
Hala kopya olabilir inner
yukarıdaki örnekte olduğu gibi, ama şimdi bir nesne ve beri değil ilkel, referans gelen newOuter.inner
kopyalanır outer.inner
yerel ile sona erecek bunun yerine, hangi vasıta newOuter
doğrudan devlet nesneye bağlı nesne .
Bu, yerel olarak oluşturulan mutasyonların nesneyi (devlette) newOuter.inner
doğrudan etkileyeceği anlamına gelir outer.inner
, çünkü bunlar aslında aynı şey haline gelmiştir (bilgisayar belleğinde).
Object.assign
bu nedenle sadece ilkel tipte değerler tutan en içteki üyelere sahip nispeten basit bir seviye derin durum yapınız varsa işe yarayacaktır.
Güncellemeniz gereken daha derin nesneleriniz (2. seviye veya daha fazla) varsa kullanmayın Object.assign
. Durumu doğrudan mutasyona uğratma riskiniz vardır.
2. Lodash'ın klonu
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>
Lodash'ın cloneDeep'inin kullanımı çok daha kolaydır. Derin bir klonlama yapar , bu nedenle içeride çok seviyeli nesneler veya dizilerle oldukça karmaşık bir duruma sahipseniz, sağlam bir seçenektir. Sadece cloneDeep()
üst düzey durum özelliği, klonlanan parçayı istediğiniz şekilde değiştirin ve setOuter()
tekrar duruma getirin.
3. değişmezlik yardımcısı
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
yepyeni bir seviyeye alır ve bu konuda serin şey olabilir sadece olmasıdır $set
devlet öğelere değerleri, aynı zamanda $push
, $splice
, $merge
(vs) onları. İşte mevcut komutların bir listesi .
Yan notlar
Yine, derinden iç içe geçmiş ( ) değil, durum nesnesinin ( bu örneklerde) setOuter
yalnızca birinci düzey özelliklerini değiştirdiğini unutmayın . Farklı bir şekilde davransaydı, bu soru olmazdı.outer
outer.inner
Hangisi projeniz için doğru ?
Harici bağımlılıklar istemiyorsanız veya kullanamıyorsanız ve basit bir durum yapısına sahipseniz , sadık kalın Object.assign
.
Büyük ve / veya karmaşık bir durumu manipüle ederseniz , Lodash's cloneDeep
akıllıca bir seçimdir.
Gelişmiş yeteneklere ihtiyacınız varsa , yani devlet yapınız karmaşıksa ve üzerinde her türlü işlemi yapmanız gerekiyorsa, immutability-helper
durum manipülasyonu için kullanılabilen çok gelişmiş bir araçtır.
... yoksa bunu gerçekten yapmak zorunda mısın ?
React'ın durumunda karmaşık bir veri tutarsanız, belki de bu veriyi ele almanın diğer yollarını düşünmek için iyi bir zamandır. React bileşenlerinde karmaşık bir durum nesnesini doğru ayarlamak basit bir işlem değildir ve farklı yaklaşımları düşünmenizi şiddetle tavsiye ederim.
Büyük olasılıkla karmaşık verilerinizi bir Redux mağazasında tutmanız, redüktörler ve / veya sagalar kullanarak orada ayarlamanız ve seçiciler kullanarak erişmeniz daha iyi olur.