सरणियों पर चलने के दौरान सावधान रहें !!
यह एक आम गलत धारणा है कि एरे में तत्व के सूचकांक का उपयोग करना उस त्रुटि को दबाने का एक स्वीकार्य तरीका है जिससे आप संभवतः परिचित हैं:
Each child in an array should have a unique "key" prop.
हालांकि, कई मामलों में यह नहीं है! यह एंटी-पैटर्न है जो कुछ स्थितियों में अवांछित व्यवहार को जन्म दे सकता है ।
key
प्रोप को समझना
प्रतिक्रिया key
घटक-से-डोम तत्व संबंध को समझने के लिए प्रोप का उपयोग करती है , जो तब सुलह प्रक्रिया के लिए उपयोग की जाती है । इसलिए यह बहुत महत्वपूर्ण है कि कुंजी हमेशा अद्वितीय बनी रहे , अन्यथा एक अच्छा मौका है रिएक्ट तत्वों को मिलाएगा और गलत को म्यूट करेगा। यह भी महत्वपूर्ण है कि सर्वश्रेष्ठ प्रदर्शन बनाए रखने के लिए ये कुंजी सभी री-रेंडरर्स में स्थिर रहें ।
कहा जा रहा है, किसी को हमेशा उपरोक्त को लागू करने की आवश्यकता नहीं है, बशर्ते कि यह ज्ञात हो कि सरणी पूरी तरह से स्थिर है। हालांकि, जब भी संभव हो, सर्वोत्तम प्रथाओं को लागू करने को प्रोत्साहित किया जाता है।
इस GitHub मुद्दे में एक रिएक्टर डेवलपर ने कहा :
- कुंजी वास्तव में प्रदर्शन के बारे में नहीं है, यह पहचान के बारे में अधिक है (जो बदले में बेहतर प्रदर्शन की ओर जाता है)। बेतरतीब ढंग से सौंपा और बदलते मूल्यों की पहचान नहीं है
- हम यह जानकर कि आपका डेटा कैसे बनाया जाता है, हम वास्तविक रूप से [स्वचालित रूप से] कुंजी प्रदान नहीं कर सकते। मेरा सुझाव है कि यदि आपके पास आईडी नहीं है तो आप किसी प्रकार के हैशिंग फ़ंक्शन का उपयोग कर सकते हैं
- जब हम सरणियों का उपयोग करते हैं तो हमारे पास पहले से ही आंतरिक कुंजी होती है, लेकिन वे सरणी में सूचकांक होते हैं। जब आप एक नया तत्व सम्मिलित करते हैं, तो वे कुंजी गलत हैं।
संक्षेप में, एक key
होना चाहिए:
- अद्वितीय - एक कुंजी एक भाई घटक के समान नहीं हो सकती है ।
- स्टेटिक - एक कुंजी को रेंडरर्स के बीच कभी नहीं बदलना चाहिए।
key
प्रोप का उपयोग करना
ऊपर दिए गए स्पष्टीकरण के अनुसार, निम्नलिखित नमूनों का ध्यानपूर्वक अध्ययन करें और जब संभव हो, अनुशंसित दृष्टिकोण को लागू करने का प्रयास करें।
खराब (संभावित रूप से)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
यह यकीनन सबसे आम गलती है जब रिएक्ट में एक सरणी पर पुनरावृति होती है। यह दृष्टिकोण तकनीकी रूप से "गलत" नहीं है , यह सिर्फ ... "खतरनाक" है यदि आप नहीं जानते कि आप क्या कर रहे हैं। यदि आप एक स्थिर सरणी के माध्यम से पुनरावृत्ति कर रहे हैं, तो यह पूरी तरह से वैध दृष्टिकोण है (उदाहरण के लिए आपके नेविगेशन मेनू में लिंक की एक सरणी)। हालांकि, यदि आप वस्तुओं को जोड़ रहे हैं, हटा रहे हैं, पुन: व्यवस्थित कर रहे हैं या फ़िल्टर कर रहे हैं, तो आपको सावधान रहने की आवश्यकता है। आधिकारिक प्रलेखन में इस विस्तृत विवरण पर एक नज़र डालें ।
class MyApp extends React.Component {
constructor() {
super();
this.state = {
arr: ["Item 1"]
}
}
click = () => {
this.setState({
arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
});
}
render() {
return(
<div>
<button onClick={this.click}>Add</button>
<ul>
{this.state.arr.map(
(item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
)}
</ul>
</div>
);
}
}
const Item = (props) => {
return (
<li>
<label>{props.children}</label>
<input value={props.text} />
</li>
);
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
इस स्निपेट में हम एक गैर-स्थैतिक सरणी का उपयोग कर रहे हैं और हम इसे स्टैक के रूप में उपयोग करने के लिए खुद को प्रतिबंधित नहीं कर रहे हैं। यह एक असुरक्षित दृष्टिकोण है (आप देखेंगे कि क्यों)। ध्यान दें कि कैसे हम सरणी (मूल रूप से अनशिफ्ट) की शुरुआत में आइटम जोड़ते हैं, प्रत्येक के लिए मान <input>
जगह पर रहता है। क्यों? क्योंकि key
प्रत्येक वस्तु की विशिष्ट पहचान नहीं है।
दूसरे शब्दों में, पहली बार Item 1
में key={0}
। जब हम दूसरा आइटम जोड़ते हैं, तो शीर्ष आइटम बन जाता है Item 2
, उसके बाद Item 1
दूसरा आइटम बन जाता है । हालांकि, अब Item 1
है key={1}
और न key={0}
अब। इसके बजाय, Item 2
अब है key={0}
!!
जैसे, रिएक्ट को लगता है कि <input>
तत्व नहीं बदले हैं, क्योंकि Item
कुंजी 0
हमेशा शीर्ष पर है!
तो यह दृष्टिकोण कभी-कभी केवल बुरा क्यों होता है ?
यह दृष्टिकोण केवल जोखिम भरा है यदि सरणी को किसी तरह से फ़िल्टर किया जाता है, पुनर्व्यवस्थित किया जाता है, या आइटम जोड़े / हटाए जाते हैं। यदि यह हमेशा स्थिर होता है, तो यह उपयोग करने के लिए पूरी तरह से सुरक्षित है। उदाहरण के लिए, इस तरह के साथ एक नेविगेशन मेनू ["Home", "Products", "Contact us"]
को सुरक्षित रूप से पुनरावृत्त किया जा सकता है क्योंकि आप शायद कभी नए लिंक नहीं जोड़ेंगे या उन्हें पुनर्व्यवस्थित नहीं करेंगे।
संक्षेप में, यहाँ जब आप सुरक्षित रूप से सूचकांक का उपयोग कर सकते हैंkey
:
- सरणी स्थिर है और कभी नहीं बदलेगी।
- सरणी को कभी भी फ़िल्टर नहीं किया जाता है (सरणी का सबसेट प्रदर्शित करें)।
- सरणी को कभी भी पुन: व्यवस्थित नहीं किया जाता है।
- सरणी का उपयोग स्टैक या LIFO के रूप में किया जाता है (अंतिम में, पहले आउट)। दूसरे शब्दों में, जोड़ना केवल सरणी के अंत में किया जा सकता है (यानी पुश), और केवल अंतिम आइटम को कभी भी हटाया जा सकता है (यानी पॉप)।
इसके बजाय, हमने ऊपर के स्निपेट में, जोड़े गए आइटम को सरणी के अंत में धकेल दिया , प्रत्येक मौजूदा आइटम के लिए क्रम हमेशा सही होगा।
बहुत बुरा
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
हालांकि यह दृष्टिकोण संभवतः चाबियों की विशिष्टता की गारंटी देगा, यह हमेशा सूची में प्रत्येक आइटम को फिर से प्रस्तुत करने के लिए प्रतिक्रिया देगा , भले ही यह आवश्यक न हो। यह एक बहुत बुरा समाधान है क्योंकि यह प्रदर्शन को बहुत प्रभावित करता है। यह उल्लेख नहीं करने के लिए कि कोई भी उस घटना में एक महत्वपूर्ण टकराव की संभावना को बाहर नहीं कर सकता है जो एक Math.random()
ही संख्या को दो बार पैदा करता है।
अस्थिर कुंजी (जैसे उनके द्वारा उत्पादित Math.random()
) कई घटक उदाहरणों और डोम नोड्स को अनावश्यक रूप से पुनः निर्मित करने का कारण बनेगी, जिससे बच्चे के घटकों में प्रदर्शन में गिरावट और खोई हुई स्थिति हो सकती है।
बहुत अच्छा
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
यह यकीनन सबसे अच्छा तरीका है क्योंकि यह ऐसी संपत्ति का उपयोग करता है जो डेटासेट में प्रत्येक आइटम के लिए अद्वितीय है। उदाहरण के लिए, यदि rows
किसी डेटाबेस से प्राप्त डेटा शामिल है , तो कोई तालिका की प्राथमिक कुंजी का उपयोग कर सकता है ( जो आमतौर पर एक ऑटो-वृद्धि संख्या है )।
कुंजी चुनने का सबसे अच्छा तरीका एक स्ट्रिंग का उपयोग करना है जो विशिष्ट रूप से अपने भाई-बहनों के बीच एक सूची आइटम की पहचान करता है। अक्सर आप चाबी के रूप में अपने डेटा से आईडी का उपयोग करेंगे
अच्छा
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
यह भी एक अच्छा तरीका है। यदि आपके डेटासेट में कोई डेटा नहीं है जो विशिष्टता की गारंटी देता है ( जैसे कि मनमानी संख्या की एक सरणी ), तो कुंजी टकराने की संभावना है। इस तरह के मामलों में, इसके ऊपर पुनरावृत्ति करने से पहले डेटासेट में प्रत्येक आइटम के लिए एक विशिष्ट पहचानकर्ता को मैन्युअल रूप से जनरेट करना सबसे अच्छा है। अधिमानतः घटक को बढ़ते समय या जब डेटासेट प्राप्त होता है ( उदाहरण के लिए props
या async एपीआई कॉल से ), तो केवल एक बार ऐसा करने के लिए , और प्रत्येक बार घटक पुन: रेंडर करने पर नहीं। वहां पहले से ही मुट्ठी भर पुस्तकालय हैं जो आपको ऐसी चाबियां प्रदान कर सकते हैं। यहाँ एक उदाहरण है: प्रतिक्रिया-की-इंडेक्स ।
key
संपत्ति होनी चाहिए । यह ReactJS को उपयुक्त DOM नोड्स के संदर्भ खोजने और मार्क-अप के अंदर केवल सामग्री अपडेट करने में मदद करेगा, लेकिन पूरे टेबल / पंक्ति को फिर से प्रस्तुत नहीं करेगा।