वेनिला जावास्क्रिप्ट का: राज्य, आम तौर पर तीन तरीकों का इस्तेमाल किया जाता है प्रतिक्रिया में गहराई से नेस्टेड वस्तुओं / चर को संशोधित करने Object.assign
, अचल स्थिति-हेल्पर और cloneDeep
से Lodash ।
इसे प्राप्त करने के लिए अन्य कम लोकप्रिय तीसरे पक्ष के काम भी बहुत हैं, लेकिन इस उत्तर में, मैं इन तीन विकल्पों को कवर करूंगा। इसके अलावा, कुछ अतिरिक्त वेनिला जावास्क्रिप्ट विधियाँ मौजूद हैं, जैसे सरणी का प्रसार, (उदाहरण के लिए @ एमपीएन का जवाब देखें), लेकिन वे बहुत सहज, सभी राज्य हेरफेर स्थितियों को संभालने के लिए उपयोग करने में सक्षम और सक्षम नहीं हैं।
जैसा कि शीर्ष मतों के जवाब में असंख्य बार बताया गया था, जिनके लेखक राज्य के प्रत्यक्ष उत्परिवर्तन का प्रस्ताव रखते हैं: बस ऐसा मत करो । यह एक सर्वव्यापी रिएक्ट विरोधी पैटर्न है, जो अनिवार्य रूप से अवांछित परिणामों को जन्म देगा। सही तरीका सीखें।
आइए तीन व्यापक रूप से उपयोग किए जाने वाले तरीकों की तुलना करें।
इस राज्य वस्तु संरचना को देखते हुए:
state = {
outer: {
inner: 'initial value'
}
}
inner
राज्य के बाकी हिस्सों को प्रभावित किए बिना आंतरिक- क्षेत्र के मूल्य को अपडेट करने के लिए आप निम्न विधियों का उपयोग कर सकते हैं ।
1. वेनिला जावास्क्रिप्ट ऑब्जेक्ट
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>
ध्यान रखें, कि Object.assign एक गहरी क्लोनिंग का प्रदर्शन नहीं करेगा , क्योंकि यह केवल संपत्ति मूल्यों की प्रतिलिपि बनाता है , और इसीलिए यह ऐसा करता है जिसे उथला नकल कहा जाता है (टिप्पणियां देखें)।
इस काम के लिए, हमें केवल आदिम प्रकारों ( outer.inner
) के गुणों में हेरफेर करना चाहिए , जो कि तार, संख्या, बूलियन हैं।
इस उदाहरण में, हम एक नई निरंतर (बना रहे हैं const newOuter...
) का उपयोग किया Object.assign
है, जो एक खाली वस्तु बनाता है ( {}
), प्रतियां outer
(वस्तु { inner: 'initial value' }
) में और फिर प्रतियां एक अलग वस्तु { inner: 'updated value' }
से अधिक यह।
इस तरह, अंत में नव निर्मित newOuter
स्थिरांक एक मूल्य धारण करेगा { inner: 'updated value' }
क्योंकि inner
संपत्ति अतिप्राप्त हो गई। यह newOuter
एक बिल्कुल नया ऑब्जेक्ट है, जो राज्य में ऑब्जेक्ट से जुड़ा हुआ नहीं है, इसलिए इसे आवश्यकतानुसार म्यूट किया जा सकता है और राज्य समान रहेगा और इसे तब तक नहीं बदला जाएगा जब तक कि इसे चलाने के लिए कमांड अपडेट न हो जाए।
अंतिम भाग राज्य में setOuter()
मूल outer
को एक नई निर्मित newOuter
वस्तु के साथ बदलने के लिए सेटर का उपयोग करना है (केवल मूल्य बदल जाएगा, संपत्ति का नाम outer
नहीं होगा)।
अब कल्पना करें कि हमारे पास और अधिक गहरा राज्य है state = { outer: { inner: { innerMost: 'initial value' } } }
। हम newOuter
ऑब्जेक्ट बनाने की कोशिश कर सकते हैं और इसे outer
राज्य से सामग्री के साथ पॉप्युलेट कर सकते हैं, लेकिन इस नए बनाए गए ऑब्जेक्ट के मूल्य को Object.assign
कॉपी नहीं कर पाएंगे क्योंकि यह बहुत गहराई से नेस्टेड है।innerMost
newOuter
innerMost
तुम अब भी नकल कर सकता है inner
उपरोक्त उदाहरण की तरह, है, लेकिन यह अब एक वस्तु और है, क्योंकि न एक आदिम, संदर्भ से newOuter.inner
कॉपी किया जायेगा outer.inner
बजाय, जिसका अर्थ है कि हम स्थानीय के साथ खत्म हो जाएगा newOuter
वस्तु सीधे राज्य में वस्तु से बंधा ।
इसका मतलब है कि इस मामले में स्थानीय रूप से बनाए गए म्यूटेशन newOuter.inner
सीधे outer.inner
ऑब्जेक्ट (राज्य में) को प्रभावित करेंगे , क्योंकि वे वास्तव में एक ही चीज (कंप्यूटर की मेमोरी में) बन गए हैं।
Object.assign
इसलिए आप केवल तभी काम करेंगे जब आपके पास आदिम प्रकार के मूल्यों को रखने वाले अंतरतम सदस्यों के साथ अपेक्षाकृत एक स्तर की गहरी राज्य संरचना हो।
यदि आपके पास गहरी वस्तुएं (दूसरा स्तर या अधिक) हैं, जिन्हें आपको अपडेट करना चाहिए, उपयोग न करें Object.assign
। आप सीधे राज्य को म्यूट करने का जोखिम उठाते हैं।
2. लोधश का क्लोन
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()
शीर्ष-स्तरीय राज्य संपत्ति, जो भी आप चाहते हैं, उसमें क्लोन किए गए भाग को म्यूट करें और setOuter()
इसे वापस राज्य में लाएं।
3. अपरिहार्यता-सहायक
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
यह एक बिल्कुल नए स्तर पर ले जाता है, और इसके बारे में अच्छी बात यह है कि यह न केवल कर सकते हैं $set
राज्य आइटम को महत्व देता है, लेकिन यह भी $push
, $splice
, $merge
(आदि) उन्हें। यहां उपलब्ध आदेशों की एक सूची दी गई है ।
साइड नोट्स
फिर, ध्यान रखें, कि setOuter
केवल राज्य वस्तु के प्रथम-स्तरीय गुणों को संशोधित करता है ( outer
इन उदाहरणों में), न कि गहन रूप से नेस्टेड ( outer.inner
)। यदि यह एक अलग तरीके से व्यवहार करता है, तो यह प्रश्न मौजूद नहीं होगा।
आपके प्रोजेक्ट के लिए कौन सा सही है ?
यदि आप बाहरी निर्भरता का उपयोग नहीं करते हैं या नहीं कर सकते हैं , और एक सरल राज्य संरचना है , तो छड़ी करें Object.assign
।
यदि आप एक विशाल और / या जटिल स्थिति में हेरफेर करते हैं , तो लोदश cloneDeep
एक बुद्धिमान विकल्प है।
यदि आपको उन्नत क्षमताओं की आवश्यकता है , अर्थात यदि आपका राज्य संरचना जटिल है और आपको उस पर सभी प्रकार के संचालन करने की आवश्यकता है, तो कोशिश करें immutability-helper
, यह एक बहुत ही उन्नत उपकरण है जिसका उपयोग राज्य हेरफेर के लिए किया जा सकता है।
... या, क्या आपको वास्तव में ऐसा करने की आवश्यकता है?
यदि आप प्रतिक्रिया की स्थिति में एक जटिल डेटा रखते हैं, तो शायद इसे संभालने के अन्य तरीकों के बारे में सोचने का यह अच्छा समय है। रिएक्ट घटकों में एक जटिल राज्य वस्तुओं को सही सेट करना एक सीधा ऑपरेशन नहीं है, और मैं दृढ़ता से विभिन्न दृष्टिकोणों के बारे में सोचने का सुझाव देता हूं।
सबसे अधिक संभावना है कि आप अपने जटिल डेटा को Redux स्टोर में रखना बेहतर मानते हैं, इसे वहां रीड्यूसर और / या सागा का उपयोग करके सेट करते हैं और चयनकर्ताओं का उपयोग करके इसे एक्सेस करते हैं।