वो परेशान करने वाला बग जो हर React डेवलपर को सताता है
\nक्या आपने कभी ब्राउज़र कंसोल में यह चेतावनी देखी है Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.? अगर हाँ — तो आप अकेले नहीं हैं। Stack Overflow पर इस सवाल के हज़ारों व्यूज़ हैं और यह React के नए और अनुभवी डेवलपर्स दोनों के लिए सिरदर्द बन गया है।
इस लेख में हम समझेंगे कि यह एरर क्यों आती है, TypeScript के साथ इसे कैसे ठीक करें, और सबसे ज़रूरी — अपने ऐप की परफॉरमेंस को कैसे ऑप्टिमाइज़ करें ताकि ये चेतावनियाँ फिर कभी न दिखें। चलिए शुरू करते हैं!
\n\nReact unmounted component पर क्यों चिल्लाता है?
\nसोचिए: यूज़र ने एक पेज खोला, डेटा लोड करना शुरू किया (जैसे fetch या axios से), लेकिन जवाब आने से पहले ही दूसरे स्क्रीन पर चला गया। जिस कम्पोनेंट ने रिक्वेस्ट शुरू की थी, वह अब अनमाउंट हो चुका है (DOM से हटा दिया गया), लेकिन async ऑपरेशन अभी भी चल रहा है। जब जवाब आता है, कोड एक गैर-मौजूद कम्पोनेंट के state को अपडेट करने की कोशिश करता है। React सही ही गुस्सा होता है: "अरे, मैं उसे अपडेट नहीं कर सकता जो मौजूद ही नहीं है!"
TypeScript में यह समस्या और भी ज़्यादा दिखती है अगर आप सख्त टाइपिंग का उपयोग करते हैं और useEffect में cleanup-फंक्शन भूल जाते हैं।
समस्या का क्लासिक उदाहरण (TypeScript)
\nimport React, { useState, useEffect } from 'react';\n\ninterface User {\n id: number;\n name: string;\n}\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n const [user, setUser] = useState<User | null>(null);\n\n useEffect(() => {\n fetch(`/api/users/${userId}`)\n .then(res => res.json())\n .then(data => setUser(data)); // एरर! कम्पोनेंट अनमाउंट हो सकता है\n }, [userId]);\n\n return <div>{user?.name}</div>;\n};\nअगर यूज़र जल्दी से रूट बदल देता है, तो UserProfile कम्पोनेंट हट जाएगा, और setUser बेकार में कॉल होगा। इससे मेमोरी लीक और कंसोल में एरर आती है।
एरर ठीक करने के 3 तरीके (TypeScript उदाहरणों के साथ)
\nआइए तीन भरोसेमंद तरीके देखें जो समस्या को हल करते हैं और आपके React ऐप की परफॉरमेंस को बेहतर बनाते हैं।
\n\nतरीका 1: useRef के साथ फ्लैग (सबसे लोकप्रिय)
\nएक ref-फ्लैग बनाएँ जो बताता है कि कम्पोनेंट माउंटेड है या नहीं। State को तभी अपडेट करें जब फ्लैग true हो।
import React, { useState, useEffect, useRef } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n const [user, setUser] = useState<User | null>(null);\n const isMounted = useRef(true);\n\n useEffect(() => {\n // हर माउंट पर फ्लैग रीसेट करें\n isMounted.current = true;\n\n fetch(`/api/users/${userId}`)\n .then(res => res.json())\n .then(data => {\n if (isMounted.current) {\n setUser(data);\n }\n });\n\n // Cleanup: अनमाउंट होने पर फ्लैग को false करें\n return () => {\n isMounted.current = false;\n };\n }, [userId]);\n\n return <div>{user?.name}</div>;\n};\nफायदे: सरल, किसी एक्स्ट्रा लाइब्रेरी की ज़रूरत नहीं। नुकसान: cleanup लिखना न भूलें।
\n\nतरीका 2: fetch रिक्वेस्ट को कैंसल करने के लिए AbortController (सुझाया गया)
\nआधुनिक और सही तरीका — जवाब को इग्नोर करने के बजाय रिक्वेस्ट को ही कैंसल करें। इससे सर्वर और क्लाइंट दोनों के संसाधन बचते हैं।
\nimport React, { useState, useEffect } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n const controller = new AbortController();\n const signal = con