समस्या और प्रासंगिक संदर्भ
एक React 19.2 डैशबोर्ड पर विचार करें जो लाइव ऑर्डर्स की सदस्यता लेता है:
function LiveOrders({ tenantId }: { tenantId: string }) {
const [filter, setFilter] = useState("all")
const [count, setCount] = useState(0)
useEffect(() => {
const connection = connect(tenantId)
connection.on("order", (order) => {
if (matches(order, filter)) {
setCount(count + 1)
}
})
return () => connection.close()
}, [])
// Rendering and controls omitted.
}यूज़र द्वारा फ़िल्टर बदलने के बाद भी, कॉलबैक "all" ही लागू करता रहता है। एक ही समय में कई ऑर्डर्स आने (burst) पर काउंट 1 पर ही अटका रह सकता है। यदि कंपोनेंट को एक अलग tenantId प्राप्त होता है, तब भी यह पहले टेनेंट से ही जुड़ा रहता है। डिपेंडेंसी लिस्ट में संदर्भित प्रत्येक वैल्यू को जोड़ने से ताज़ा वैल्यू मिलने की समस्या हल होती दिखती है, लेकिन फिर हर काउंट या फ़िल्टर परिवर्तन पर कनेक्शन बंद होकर दोबारा बनने लगता है।
समझाइए कि ये विफलताएं क्यों होती हैं, यह कैसे साबित करें कि कौन सी वैल्यू बासी (stale) है, और कॉलबैक्स द्वारा नवीनतम कमिटेड वैल्यूज को देखते हुए सब्सक्रिप्शन के अभीष्ट लाइफ़टाइम को कैसे बनाए रखें।
यह एक यथार्थवादी फ़्रंटएंड इंटरव्यू प्रश्न है क्योंकि अंग्रेजी और चीनी दोनों भाषाओं में सार्वजनिक React इंटरव्यू गाइड स्पष्ट रूप से stale closures, Hook नियमों और useEffect डिपेंडेंसीज का परीक्षण करते हैं। गहरा कौशल किसी एक वर्कअराउंड को याद रखना नहीं है। यह तय करना है कि क्या किसी वैल्यू को Effect को रीस्टार्ट करना चाहिए, स्टेट ट्रांज़िशन में भाग लेना चाहिए, या Effect के स्वामित्व वाले कॉलबैक द्वारा गैर-प्रतिक्रियाशील (non-reactively) रूप से पढ़ा जाना चाहिए।
इंटरव्यूअर क्या मूल्यांकन कर रहा है
पहला, क्या उम्मीदवार तंत्र को सटीक रूप से समझा सकता है? प्रत्येक रेंडर को प्रॉप्स और स्टेट का एक स्नैपशॉट प्राप्त होता है। उस रेंडर के दौरान बनाए गए फ़ंक्शंस उस स्नैपशॉट पर क्लोज़र बनाते हैं। React बाद के रेंडर के बाद स्थानीय filter, count, या tenantId वेरिएबल्स को म्यूटेट नहीं करता है। यदि कोई बाहरी सिस्टम पहले कॉलबैक को बनाए रखता है, तो वह कॉलबैक पहले रेंडर की वैल्यूज को ही पढ़ता रहता है। क्लोज़र सामान्य रूप से व्यवहार कर रहा है; Effect का घोषित सिंक्रोनाइज़ेशन अनुबंध अपूर्ण है।
दूसरा, क्या उम्मीदवार चार अलग-अलग आवश्यकताओं को अलग कर सकता है?
| आवश्यकता | सही टूल | यह क्या बदलता है |
|---|---|---|
| बाहरी संसाधन को एक रिएक्टिव वैल्यू का पालन करना चाहिए | Effect dependency | Effect को क्लीन अप और पुनः सिंक्रोनाइज़ करता है |
| अगली स्टेट पिछली स्टेट से प्राप्त होती है | Functional updater | React की कतारबद्ध (queued) वर्तमान स्टेट से गणना करता है |
| Effect के स्वामित्व वाले कॉलबैक को पुनः सब्सक्राइब किए बिना नवीनतम कमिटेड वैल्यूज की आवश्यकता होती है | React 19.2 में useEffectEvent | वर्तमान प्रॉप्स और स्टेट को गैर-प्रतिक्रियाशील रूप से पढ़ता है |
| अनिवार्य (imperative), नॉन-रेंडर्ड म्यूटेबल डेटा को स्थिर पहचान की आवश्यकता होती है | Ref | मैन्युअल रूप से सिंक्रोनाइज़ की गई म्यूटेबल वैल्यू को स्टोर करता है |
तीसरा, क्या उम्मीदवार गलत समाधानों से बच सकता है? useCallback(fn, []) फ़ंक्शन पहचान को सुरक्षित रखता है लेकिन इसके पहले-रेंडर क्लोज़र को भी सुरक्षित रखता है। exhaustive-deps को दबाना सिमेंटिक्स को बदलने के बजाय सबूतों को छुपाता है। प्रत्येक काउंटर इंक्रीमेंट के लिए WebSocket को फिर से बनाना वैल्यूज को ताज़ा रख सकता है लेकिन यह संसाधन का गलत लाइफ़टाइम है।
अंत में, क्या उम्मीदवार क्लीनअप और एसिंक्रोनस क्रम को कवर कर सकता है? एक ताज़ा क्लोज़र पुराने अनुरोध को रद्द नहीं करता है, क्रम से बाहर (out-of-order) आए रिस्पॉन्स को नहीं रोकता है, या लीक हुए इवेंट लिसनर को नहीं हटाता है। वे अलग समवर्ती (concurrency) और लाइफ़साइकिल दायित्व हैं।
उत्तर देने से पहले स्पष्टीकरण प्रश्न
- कौन सी वैल्यूज सब्सक्रिप्शन पहचान को परिभाषित करती हैं? यदि
tenantIdसर्वर-साइड स्ट्रीम का चयन करता है, तो इसे बदलने पर पुनः कनेक्ट होना चाहिए। एक डिस्प्ले फ़िल्टर को आमतौर पर ऐसा नहीं करना चाहिए। - क्या फ़िल्टर बदलने पर ऐतिहासिक इवेंट्स का पुनर्मूल्यांकन किया जाना चाहिए? यह तय करता है कि फ़िल्टर केवल कॉलबैक से संबंधित है या यह एक नई क्वेरी या सब्सक्रिप्शन को भी ट्रिगर करता है।
- क्या कॉलबैक पिछली स्टेट से गणना करता है? काउंट को इंक्रीमेंट करने के लिए फ़ंक्शनल अपडेटर का उपयोग किया जाना चाहिए, भले ही कॉलबैक अन्यथा ताज़ा वैल्यूज पढ़ता हो।
- कौन सा React वर्ज़न तैनात है?
useEffectEventReact 19.2 में उपलब्ध है। पुराने वर्ज़न पर, सावधानीपूर्वक सिंक्रोनाइज़ किया गया ref अनुकूलता के लिए फ़ॉलबैक हो सकता है। - क्या बाहरी API पुनः कनेक्ट किए बिना अपने लिसनर को बदल सकता है? कुछ लाइब्रेरीज़ अलग
subscribeऔरupdateHandlerऑपरेशंस को एक्सपोज़ करती हैं, जो एक अलग न्यूनतम लाइफ़टाइम का समर्थन कर सकती हैं। - क्या कॉलबैक द्वारा एसिंक्रोनस अनुरोध शुरू किए जाते हैं? यदि हां, तो कैप्चर की गई वैल्यूज को हल करने के अलावा कैंसिलेशन या अनुरोध जेनरेशन चेक को परिभाषित करें।
- क्या डेवलपमेंट में Strict Mode सक्षम है? इसका अतिरिक्त सेटअप और क्लीनअप चक्र छूटे हुए क्लीनअप को उजागर कर सकता है, लेकिन यह stale closures नहीं बनाता है।
30-सेकंड का उत्तर ढांचा
"एक React कॉलबैक उस रेंडर से प्रॉप्स और स्टेट स्नैपशॉट को पढ़ता है जिसने इसे बनाया था। यहाँ Effect केवल एक बार चलता है, इसलिए कनेक्शन पहले टेनेंट, फ़िल्टर और काउंट को बनाए रखता है। मैं सब्सक्रिप्शन को tenantId पर निर्भर बनाऊंगा, क्योंकि वह वैल्यू बदलती है कि हम किस संसाधन से कनेक्ट होते हैं। मैं setCount(current => current + 1) के साथ काउंट को अपडेट करूंगा, क्योंकि यह पिछली स्टेट से प्राप्त होता है। ऑर्डर कॉलबैक द्वारा बिना रीकनेक्ट किए नवीनतम फ़िल्टर को पढ़ने के लिए, React 19.2 पर मैं उस नॉन-रिएक्टिव लॉजिक को useEffectEvent में रखूंगा और इसे Effect के स्वामित्व वाले लिसनर से कॉल करूंगा। मैं डिपेंडेंसी लिंटर को दबाऊंगा नहीं या ताज़ा रखने के लिए useCallback([]) का उपयोग नहीं करूंगा। नॉन-रेंडर्ड म्यूटेबल डेटा के लिए Refs एक मैन्युअल अनुकूलता विकल्प हैं, और async परिणामों को अभी भी कैंसिलेशन या वर्ज़निंग की आवश्यकता होती है।"
चरण-दर-चरण गहन विश्लेषण
चरण एक: प्रत्येक रेंडर को एक इम्यूटेबल स्नैपशॉट के रूप में मॉडल करें।
पहले रेंडर के दौरान, मान लें कि ये वैल्यूज हैं:
tenantId = "tenant-a"
filter = "all"
count = 0Effect एक कनेक्शन और एक कॉलबैक बनाता है जो ठीक उन्हीं स्थानीय वेरिएबल्स को संदर्भित करता है। बाद में, setFilter("paid") एक नए filter वेरिएबल के साथ दूसरा रेंडर तैयार करता है। यह मूल कॉलबैक द्वारा कैप्चर किए गए वेरिएबल को संपादित नहीं करता है। चूंकि खाली डिपेंडेंसी सूची कहती है कि Effect को फिर कभी सिंक्रोनाइज़ करने की आवश्यकता नहीं है, इसलिए बाहरी कनेक्शन मूल कॉलबैक का स्वामित्व बनाए रखता है।
यह बिना किसी अनुमान के तीनों बग्स की सटीक भविष्यवाणी करता है:
matches(order, filter)लगातार"all"का उपयोग करता रहता है।- प्रत्येक मैचिंग इवेंट
setCount(0 + 1)को कॉल करता है, इसलिए बार-बार होने वाले इवेंट्स इंक्रीमेंट्स को संयोजित करने के बजाय स्टेट को1से बदल देते हैं। - प्रॉप परिवर्तन के बाद भी कनेक्शन
"tenant-a"से जुड़ा रहता है।
एक उपयोगी डिबगिंग लॉग में रेंडर सीक्वेंस नंबर, रेंडर के दौरान देखी गई वैल्यूज और रिटेन किए गए कॉलबैक के अंदर देखी गई वैल्यूज शामिल होती हैं। कनेक्शन ID को अलग से लॉग करें। इससे पता चलता है कि कॉलबैक बासी है, सब्सक्रिप्शन रीस्टार्ट नहीं हुआ, या सर्वर ने अप्रत्याशित डेटा भेजा।
चरण दो: डिपेंडेंसीज संपादित करने से पहले सिमेंटिक्स के आधार पर प्रत्येक रीड को वर्गीकृत करें।
पूछें कि Effect प्रत्येक वैल्यू को क्यों पढ़ता है:
tenantIdनिर्धारित करता है कि कौन सी बाहरी स्ट्रीम सिंक्रोनाइज़ है। यह रिएक्टिव है और डिपेंडेंसी लिस्ट से संबंधित है।countकेवल अगले काउंट की गणना करने के लिए आवश्यक है। रीड को एक फ़ंक्शनल अपडेटर से बदलें, ताकि इसे अब कैप्चर करने की आवश्यकता न हो।filterप्रभावित करता है कि भविष्य के इवेंट को कैसे हैंडल किया जाता है, लेकिन इसे बदलने से टेनेंट कनेक्शन टूटना नहीं चाहिए। लिसनर को सब्सक्रिप्शन को फ़िल्टर के प्रति रिएक्टिव बनाए बिना एक नवीनतम-वैल्यू रीड की आवश्यकता होती है।
यह वर्गीकरण खाली डिपेंडेंसी लिस्ट और ऐसी डिपेंडेंसी लिस्ट के दो चरम सीमाओं को रोकता है जो हर रेंडर-संबंधी परिवर्तन पर एक महंगे संसाधन को रीस्टार्ट करती है।
चरण तीन: फ़ंक्शनल अपडेटर्स के साथ स्टेट ट्रांज़िशन को ठीक करें।
इसे बदलें:
setCount(count + 1)इसमें:
setCount((current) => current + 1)React अपडेटर को कतारबद्ध करता है और अगले रेंडर की गणना करते समय इसे वर्तमान पेंडिंग स्टेट पास करता है। इसलिए दस इवेंट्स दस इंक्रीमेंट्स को संयोजित करते हैं, भले ही उनका कॉलबैक पहले पंजीकृत किया गया हो या React अपडेट्स को बैच करता हो। यह केवल count डिपेंडेंसी को ठीक करता है। यह filter या tenantId को ताज़ा नहीं बनाता है।
चरण चार: ऐसी डिपेंडेंसीज घोषित करें जो संसाधन को वास्तविक रूप से पुनः सिंक्रोनाइज़ करती हैं।
कनेक्शन की पहचान में tenantId शामिल है, इसलिए Effect को इस पर निर्भर होना चाहिए। टेनेंट परिवर्तन पर, React पहले पिछला क्लीनअप चलाता है और फिर नया कनेक्शन सेट करता है। क्लीनअप को लिसनर्स को हटाना चाहिए या पुराने कनेक्शन को बंद करना चाहिए ताकि पिछले टेनेंट के इवेंट्स वर्तमान स्क्रीन को अपडेट न कर सकें।
filter और count को जोड़ने से तकनीकी रूप से प्रत्येक नए कॉलबैक को वर्तमान वैल्यूज मिल जाएंगी, लेकिन यह कनेक्शन के लाइफ़टाइम को भी फिर से परिभाषित करेगा। एक काउंटर अपडेट से डिस्कनेक्ट और पुनः कनेक्ट होगा। इससे इवेंट्स खो सकते हैं, ओवरलैपिंग क्लीनअप के दौरान इवेंट्स डुप्लिकेट हो सकते हैं, सर्वर-साइड कर्सर स्टेट रीसेट हो सकती है, और अनावश्यक लोड बन सकता है। एक डिपेंडेंसी लिस्ट एक व्यावहारिक विनिर्देश (behavioral specification) है, न कि कोई परफॉर्मेंस संकेत जिसे लिंटर के शांत होने तक संपादित किया जाए।
चरण पांच: React 19.2 में नवीनतम नॉन-रिएक्टिव रीड्स के लिए Effect Event का उपयोग करें।
सही किया गया कंपोनेंट कनेक्शन लाइफ़टाइम और कॉलबैक की ताज़गी को अलग रख सकता है:
function LiveOrders({ tenantId }: { tenantId: string }) {
const [filter, setFilter] = useState("all")
const [count, setCount] = useState(0)
const onOrder = useEffectEvent((order: Order) => {
if (matches(order, filter)) {
setCount((current) => current + 1)
}
})
useEffect(() => {
const connection = connect(tenantId)
connection.on("order", onOrder)
return () => connection.close()
}, [tenantId])
// Rendering and controls omitted.
}onOrder हमेशा नवीनतम कमिटेड filter को देखता है, जबकि Effect अभी भी केवल तभी पुनः सिंक्रोनाइज़ होता है जब tenantId बदलता है। एक Effect Event को Effect या उस Effect से जुड़े कोड से कॉल किया जाता है, जैसे कि वह लिसनर जिसे यह रजिस्टर करता है। यह एक सामान्य इवेंट-हैंडलर प्रतिस्थापन नहीं है, इसे कंपोनेंट ट्री के माध्यम से मनमाने ढंग से पास नहीं किया जाना चाहिए, और इसका उपयोग ऐसी वैल्यू को छिपाने के लिए नहीं किया जाना चाहिए जिसे वास्तव में सिंक्रोनाइज़ेशन को रीस्टार्ट करना चाहिए।
Effect Event को डिपेंडेंसी लिस्ट में न जोड़ें। वर्तमान React लिंटिंग इसकी नॉन-रिएक्टिव भूमिका को समझती है। यदि लिंटर किसी भिन्न वैल्यू की रिपोर्ट करता है, तो उस चेतावनी को डिज़ाइन फ़ीडबैक के रूप में लें और नियम को अक्षम करने के बजाय कोड संरचना को बदलें।
चरण छह: समझें कि ref कब उपयुक्त है और इसकी लागत क्या है।
React 19.2 से पहले, एक सामान्य अनुकूलता पैटर्न नवीनतम वैल्यू को एक ref में संग्रहीत करता है:
const filterRef = useRef(filter)
useEffect(() => {
filterRef.current = filter
}, [filter])
useEffect(() => {
const connection = connect(tenantId)
connection.on("order", (order) => {
if (matches(order, filterRef.current)) {
setCount((current) => current + 1)
}
})
return () => connection.close()
}, [tenantId])ref ऑब्जेक्ट रेंडर्स के दौरान स्थिर रहता है, और current को बदलने से रेंडरिंग ट्रिगर नहीं होती है। यह refs को अनिवार्य वैल्यूज जैसे कि नवीनतम हैंडलर, टाइमर ID, या बाहरी API हैंडल के लिए उपयुक्त बनाता है जो स्वयं प्रदर्शित नहीं होता है। लागत मैन्युअल सिंक्रोनाइज़ेशन है: डिपेंडेंसी लिंटर यह साबित नहीं कर सकता कि filterRef.current सही ढंग से अपडेट किया गया है, और एक छूटा हुआ सिंक्रोनाइज़ेशन Effect चुपचाप बासी व्यवहार को फिर से पेश कर देता है। केवल रेंडर से बचने के लिए प्रदर्शित स्टेट को ref में न ले जाएं, और दस्तावेज़ीकृत इनिशियलाइज़ेशन पैटर्न को छोड़कर रेंडरिंग के दौरान refs को न पढ़ें या लिखें।
चरण सात: useCallback और मेमोइज़ेशन को उनकी उचित भूमिका में रखें।
useCallback किसी फ़ंक्शन पहचान को तब तक कैश करता है जब तक कि उसकी कोई एक डिपेंडेंसी बदल न जाए। यह तब उपयोगी होता है जब कोई मेमोइज़्ड चाइल्ड या बाहरी API पहचान की परवाह करता है। यह अपने आप नवीनतम वैल्यूज प्रदान नहीं करता है:
const onOrder = useCallback((order: Order) => {
if (matches(order, filter)) {
setCount((current) => current + 1)
}
}, [])यह वर्ज़न अभी भी शुरुआती फ़िल्टर को कैप्चर करता है। [filter] जोड़ने से फ़ंक्शन रीफ़्रेश हो जाता है, लेकिन बाहरी कनेक्शन को फिर अपने लिसनर को सही ढंग से बदलना होगा। यदि लिसनर को हटाने के लिए समान फ़ंक्शन पहचान की आवश्यकता होती है, तो सेटअप और क्लीनअप को उस रेंडर के लिए समान कॉलबैक इंस्टेंस का उपयोग करना चाहिए। मेमोइज़ेशन एक पहचान से जुड़े प्रश्न का उत्तर देता है; यह सिंक्रोनाइज़ेशन-लाइफ़टाइम के प्रश्न का उत्तर नहीं देता है।
चरण आठ: एसिंक्रोनस क्रम को अलग से हल करें।
मान लीजिए कि नवीनतम फ़िल्टर एक अनुरोध शुरू करता है। "all" अनुरोध नए "paid" अनुरोध के बाद हल हो सकता है और उसके परिणामों को अधिलेखित (overwrite) कर सकता है। नवीनतम फ़िल्टर वाला कॉलबैक भी उस पुराने अनुरोध को पूरा होने से नहीं रोक सकता है। Effect को समर्थित होने पर AbortController के साथ अप्रचलित कार्य को रद्द (abort) करना चाहिए, या प्रत्येक अनुरोध को क्रमिक रूप से बढ़ते जेनरेशन से जोड़ना चाहिए और केवल नवीनतम जेनरेशन को कमिट करना चाहिए।
क्लीनअप भी सममित (symmetrical) होना चाहिए:
- उस सेटअप द्वारा बनाए गए सटीक कनेक्शन को बंद करें;
- उस सेटअप द्वारा पंजीकृत सटीक लिसनर को हटाएं;
- इंटरवल्स और टाइमआउट्स को साफ़ करें;
- पेंडिंग एसिंक्रोनस कार्य को रद्द या अमान्य करें;
- क्लीनअप के बाद देर से आने वाले कॉलबैक्स को हानिरहित बनाएं।
Strict Mode का केवल डेवलपमेंट वाला सेटअप-क्लीनअप-सेटअप क्रम यहाँ उपयोगी प्रमाण है। डुप्लिकेट कनेक्शन एक अपूर्ण क्लीनअप का संकेत देते हैं। यह इस बात का सबूत नहीं है कि Strict Mode के कारण stale closure हुआ।
चरण नौ: एक समय में एक आयाम बदलकर व्यवहार को सत्यापित करें।
| टेस्ट | अपेक्षित परिणाम |
|---|---|
| React के फिर से रेंडर होने से पहले तीन मैचिंग ऑर्डर्स उत्सर्जित करें | काउंट में तीन की वृद्धि होती है |
filter बदलें, फिर एक ऑर्डर उत्सर्जित करें | लिसनर बिना रीकनेक्ट किए नया फ़िल्टर लागू करता है |
tenantId बदलें | पुराना कनेक्शन एक बार बंद होता है; नया टेनेंट एक बार कनेक्ट होता है |
| क्लीनअप के बाद पुराने कनेक्शन से उत्सर्जित करें | वर्तमान UI में कोई बदलाव नहीं होता |
| दो अनुरोधों को उल्टे क्रम में हल करें | केवल नवीनतम अनुरोध ही कमिट हो सकता है |
| डेवलपमेंट में Strict Mode के तहत चलाएं | सेटअप और क्लीनअप सममित रहते हैं; कोई डुप्लिकेट लिसनर नहीं |
| Hook डिपेंडेंसी लिंट नियम को फिर से सक्षम करें | कोई दबाई गई या अस्पष्टीकृत डिपेंडेंसी चेतावनी नहीं बचती है |
प्रोडक्शन में, कनेक्शन सेटअप और क्लीनअप काउंट्स, सक्रिय कनेक्शन IDs, टेनेंट स्विच, लेट-इवेंट ड्रॉप्स और निरस्त किए गए अनुरोधों को ट्रैक करें। केवल कॉलबैक की ताज़गी का निदान करने के लिए निजी ऑर्डर पेलोड को लॉग न करें।
उच्च-गुणवत्ता वाला नमूना उत्तर
"मैं React के रेंडर मॉडल से शुरुआत करूंगा। प्रत्येक रेंडर को प्रॉप्स और स्टेट का एक स्नैपशॉट मिलता है, और उस रेंडर में बनाया गया फ़ंक्शन उस स्नैपशॉट पर क्लोज़र बनाता है। चूंकि इस Effect में खाली डिपेंडेंसी लिस्ट है, इसलिए बाहरी कनेक्शन पहले रेंडर के कॉलबैक को रखता है। इसलिए यह पहले टेनेंट, फ़िल्टर और काउंट को देखता है।
मैं डिपेंडेंसी लिस्ट बदलने से पहले प्रत्येक वैल्यू को वर्गीकृत करूंगा। tenantId बाहरी संसाधन का चयन करता है, इसलिए यह डिपेंडेंसीज से संबंधित है और बदलाव होने पर पुराने कनेक्शन को बंद करके नया खोलना चाहिए। count का उपयोग केवल अगली स्टेट प्राप्त करने के लिए किया जाता है, इसलिए मैं setCount(current => current + 1) को कॉल करूंगा। यह सुनिश्चित करता है कि बर्स्ट अपडेट्स React की कतारबद्ध स्टेट से संयोजित हों। नवीनतम filter की आवश्यकता तब होती है जब Effect के स्वामित्व वाला ऑर्डर लिसनर ट्रिगर होता है, लेकिन इसे कनेक्शन को रीस्टार्ट नहीं करना चाहिए। चूंकि यह एप्लिकेशन React 19.2 का उपयोग करता है, मैं फ़िल्टरिंग लॉजिक को useEffectEvent में रखूंगा और उस Effect Event को ऐसे Effect से पंजीकृत करूंगा जो केवल tenantId पर निर्भर करता है।
मैं exhaustive-deps को नहीं दबाऊंगा, क्योंकि डिपेंडेंसी लिस्ट सिंक्रोनाइज़ेशन का वर्णन करती है। मैं ताज़गी के समाधान के रूप में useCallback([]) का उपयोग नहीं करूंगा, क्योंकि यह शुरुआती क्लोज़र को बनाए रखता है। एक पुराने React वर्ज़न पर, filter से सिंक्रोनाइज़ किया गया ref एक अनुकूलता फ़ॉलबैक हो सकता है, लेकिन वह मैन्युअल है और डिपेंडेंसी विश्लेषण के लिए अदृश्य है।
अंत में, मैं बर्स्ट इंक्रीमेंट्स, बिना रीकनेक्ट किए फ़िल्टर परिवर्तन, ठीक एक क्लीनअप और सेटअप के साथ टेनेंट परिवर्तन, बंद कनेक्शन से आने वाले इवेंट और डेवलपमेंट Strict Mode का परीक्षण करूंगा। यदि कॉलबैक्स अनुरोध शुरू करते हैं, तो मैं अप्रचलित अनुरोधों को रद्द या वर्ज़न भी करूंगा, क्योंकि अकेले क्लोज़र की ताज़गी क्रम से बाहर आने वाले परिणामों को नहीं रोकती है।"
सामान्य गलतियां
- प्रत्येक क्लोज़र को बग कहना → कॉलबैक्स से रेंडर स्नैपशॉट को कैप्चर करने की उम्मीद की जाती है → बरकरार रखे गए कॉलबैक और अभीष्ट सिंक्रोनाइज़ेशन के बीच के बेमेल की पहचान करें।
exhaustive-depsको दबाना → कोड यह वादा करता है कि रिएक्टिव रीड्स कभी मायने नहीं रखते, जबकि उनका उपयोग जारी रहता है → कोड को बदलें ताकि डिपेंडेंसी लिस्ट ईमानदारी से Effect का वर्णन करे।- प्रत्येक स्टेट वैल्यू को डिपेंडेंसीज में जोड़ना → हर अपडेट के बाद एक महंगे सब्सक्रिप्शन को दोबारा बनाकर ताज़गी बहाल की जाती है → संसाधन पहचान को नवीनतम कॉलबैक रीड्स से अलग करें।
useCallback(fn, [])का उपयोग करना → स्थिर पहचान पहले क्लोज़र को भी फ्रीज कर देती है →useCallbackको सही डिपेंडेंसीज दें या उस टूल का उपयोग करें जो वांछित सिमेंटिक्स से मेल खाता हो।- स्टेट को ref से बदलना → ref राइट्स रेंडर नहीं करते हैं और दृश्य UI से भिन्न हो सकते हैं → प्रदर्शित डेटा को स्टेट में रखें और अनिवार्य म्यूटेबल डेटा के लिए refs आरक्षित करें।
- वास्तविक डिपेंडेंसी को छिपाने के लिए
useEffectEventका उपयोग करना → जब बाहरी संसाधन बदलना चाहिए तब Effect प्रतिक्रिया नहीं देता है → संसाधन-परिभाषित करने वाली वैल्यूज को डिपेंडेंसी लिस्ट में रखें। - फ़िल्टर को ठीक करना लेकिन
setCount(count + 1)को बनाए रखना → बर्स्ट इवेंट्स अभी भी कैप्चर किए गए काउंट से रिप्लेस करते हैं → पिछली स्टेट से प्राप्त स्टेट के लिए फ़ंक्शनल अपडेटर का उपयोग करें। - क्लीनअप और अनुरोध क्रम की अनदेखी करना → लीक हुए लिसनर्स और देर से आए रिस्पॉन्स अभी भी स्क्रीन को दूषित करते हैं → इसके लाइफ़साइकिल के अनुसार प्रत्येक ऑपरेशन को हटाएं, बंद करें, रद्द करें या वर्ज़न करें।
फ़ॉलो-अप सवाल और जवाब
फ़ॉलो-अप 1: क्लिक हैंडलर के अंदर setTimeout पुरानी स्टेट वैल्यू क्यों देखता है?
टाइमआउट कॉलबैक उस रेंडर से संबंधित है जिसमें क्लिक हुआ था। स्टेट अपडेट करने से एक नया रेंडर शेड्यूल होता है; यह उस हैंडलर के स्थानीय स्टेट वेरिएबल को म्यूटेट नहीं करता है। यदि विलंबित कार्रवाई को क्लिक के समय की वैल्यू की रिपोर्ट करनी है, तो स्नैपशॉट सही है। यदि इसे नवीनतम कमिटेड वैल्यू का उपयोग करना चाहिए, तो उस आवश्यकता को Effect-संबंधित कोड में Effect Event के साथ या एक अनिवार्य कॉलबैक के लिए सावधानीपूर्वक बनाए रखे गए ref के साथ स्पष्ट रूप से मॉडल करें।
फ़ॉलो-अप 2: Effect डिपेंडेंसी लिस्ट में filter क्यों न डालें?
यह केवल तभी सही है जब फ़िल्टर बदलने से बाहरी सिस्टम को पुनः सिंक्रोनाइज़ करना चाहिए। यदि सर्वर सब्सक्रिप्शन स्वयं फ़िल्टर-विशिष्ट है, तो इसे शामिल करें और पुनः कनेक्ट करें। इस परिदृश्य में टेनेंट स्ट्रीम वही रहती है और फ़िल्टर स्थानीय इवेंट-प्रोसेसिंग नीति है, इसलिए पुनः कनेक्ट करने से गलत लाइफ़टाइम मिलेगा। चुनने से पहले उत्पाद और API धारणा बताएं।
फ़ॉलो-अप 3: क्या useEffectEvent साधारण इवेंट हैंडलर्स की जगह लेता है?
नहीं। एक बटन क्लिक सामान्य रूप से रेंडरिंग के दौरान बनाए गए इवेंट हैंडलर द्वारा नियंत्रित किया जाता है। एक Effect Event किसी Effect या उससे जुड़े कोड द्वारा लागू किए गए नॉन-रिएक्टिव लॉजिक के लिए होता है, जैसे कि टाइमर या सब्सक्रिप्शन लिसनर। इसे इसका उपयोग करने वाले Effect के लिए स्थानीय रहना चाहिए और इसे एक सामान्य कॉलबैक ट्रांसपोर्ट तंत्र नहीं बनना चाहिए।
फ़ॉलो-अप 4: क्या एक फ़ंक्शनल अपडेटर हर stale closure को हल कर सकता है?
नहीं। यह केवल एक ऐसे स्टेट ट्रांज़िशन को हल करता है जो उसी स्टेट के पिछले मान से प्राप्त होता है। यह किसी प्रॉप, किसी अन्य स्टेट वेरिएबल या बाहरी कॉन्फ़िगरेशन को ताज़ा नहीं बना सकता है। इस उदाहरण में यह काउंट इंक्रीमेंट्स को ठीक करता है लेकिन टेनेंट कनेक्शन या फ़िल्टर रीड को नहीं।
फ़ॉलो-अप 5: स्टेट की तुलना में ref कब बेहतर है?
ref का उपयोग तब करें जब किसी वैल्यू को रेंडर्स के दौरान बने रहना चाहिए, परिवर्तनों से UI रेंडर नहीं होना चाहिए, और वैल्यू का उपयोग अनिवार्य रूप से किया जाता है: एक टाइमर ID, DOM नोड, कनेक्शन हैंडल, या संगतता नवीनतम-वैल्यू सेल। यदि रेंडर किया गया आउटपुट इस पर निर्भर करता है, तो स्टेट का उपयोग करें। एक ref ताज़गी की ज़िम्मेदारी डेवलपर पर स्थानांतरित करता है, इसलिए इसके सिंक्रोनाइज़ेशन बिंदु का दस्तावेजीकरण और परीक्षण करें।
फ़ॉलो-अप 6: Strict Mode इस समस्या को डिबग करने में कैसे मदद कर सकता है?
डेवलपमेंट Strict Mode Effects के लिए एक अतिरिक्त सेटअप और क्लीनअप चक्र चलाता है। यदि दो लिसनर्स या कनेक्शन बने रहते हैं, तो क्लीनअप अधूरा है या गलत कॉलबैक पहचान का उपयोग करता है। एक बार क्लीनअप सही हो जाने पर, अतिरिक्त चक्र को एक सक्रिय सब्सक्रिप्शन छोड़ना चाहिए। बासी स्नैपशॉट स्वयं दोनों मोड्स में सामान्य JavaScript क्लोज़र सिमेंटिक्स का पालन करता है।
फ़ॉलो-अप 7: क्या होगा यदि क्लीनअप और नए कनेक्शन के बीच कोई इवेंट सक्रिय हो जाए?
बाहरी सिस्टम की डिलीवरी गारंटी को परिभाषित करें। क्लाइंट को एक रिज़्यूमेबल कर्सर, सीक्वेंस नंबर, रीप्ले विंडो, या स्नैपशॉट के बाद स्ट्रीम की आवश्यकता हो सकती है। React डिपेंडेंसी परिवर्तन स्थानीय लाइफ़टाइम का प्रबंधन कर सकता है, लेकिन यह सर्वर रीकनेक्शन के दौरान दोषरहित डिलीवरी की गारंटी नहीं दे सकता है। यह एक प्रोटोकॉल आवश्यकता है और इसका अलग से परीक्षण किया जाना चाहिए।
फ़ॉलो-अप 8: आप पुराने फ़ेच (fetch) को नए परिणामों को अधिलेखित करने से कैसे रोकते हैं?
जब API कैंसिलेशन का समर्थन करता है तो Effect क्लीनअप के दौरान अप्रचलित अनुरोध को रद्द (abort) करें। अन्यथा, प्रत्येक अनुरोध को एक जेनरेशन असाइन करें और स्टेट को केवल तभी अपडेट करें जब पूरा होने वाला जेनरेशन अभी भी वर्तमान हो। केवल नवीनतम-वैल्यू वाला ref नेटवर्क कार्य को रद्द नहीं करता है और इसे ऑर्डरिंग गारंटी के रूप में प्रस्तुत नहीं किया जाना चाहिए।