Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.रिएक्ट आपको बता रहा है कि रेंडरिंग के कारण स्टेट अपडेट हुआ, जिसके कारण रेंडर हुआ, जिसके कारण अपडेट हुआ। कारण लगभग हमेशा चार पैटर्न में से एक होता है, और प्रत्येक का एक स्पष्ट निर्धारण होता है।
📋 Table of Contents
- वास्तव में क्या हो रहा है
- कारण 1: इसे पास करने के बजाय हैंडलर को कॉल करना
- कारण 2: निर्भरता सारणी के बिना उपयोग प्रभाव
- कारण 3: एक निर्भरता जो हर रेंडर को बदल देती है
- कारण 4: मान प्राप्त करने के लिए राज्य की स्थापना करना
- सशर्त-रेंडर संस्करण
- अपराधी को ढूंढना
- त्वरित संदर्भ
- अक्सर पूछे जाने वाले प्रश्न
- निष्कर्ष
वास्तव में क्या हो रहा है
रेंडरिंग प्रॉप्स और स्टेट से यूआई की शुद्ध गणना होनी चाहिए। यदि यह स्थिति भी निर्धारित करता है, तो आप एक चक्र बनाते हैं। रिएक्ट भगोड़े का पता लगाता है और टैब को फ़्रीज़ होने देने के बजाय उसे रोकता है।
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // runs during render -> triggers render -> runs again
return <div>{count}</div>;
}
यहां त्रुटि संदेश असामान्य रूप से सटीक है: आपके रेंडर पथ में कुछ हर बार चलने पर अद्यतन स्थिति में होता है।
कारण 1: इसे पास करने के बजाय हैंडलर को कॉल करना
अब तक का सबसे आम संस्करण, और वह जो कम से कम एक बार सभी को आकर्षित करता है।
function App() {
const [count, setCount] = useState(0);
// ❌ setCount(count + 1) is CALLED during render.
// Its return value (undefined) becomes the onClick handler.
return <button onClick={setCount(count + 1)}>Increment</button>;
}
onClick बाद में कॉल करने के लिए एक फ़ंक्शन की आवश्यकता है। लेखनsetCount(...) इसे तुरंत क्रियान्वित करता है। इसे एक एरो फ़ंक्शन में लपेटें ताकि परिणाम के बजाय फ़ंक्शन पारित हो जाए।
// ✅ A function is passed; it runs on click.
<button onClick={() => setCount(count + 1)}>Increment</button>
// ✅ Or reference an existing function without calling it.
<button onClick={handleClick}>Increment</button> // no parentheses
// ❌ This calls handleClick during render.
<button onClick={handleClick()}>Increment</button>
नियम:JSX में हैंडलर नाम के बाद कोष्ठक का अर्थ है कि यह रेंडर के दौरान चलता है। यदि आपको तर्क पारित करने की आवश्यकता है, तो इसे लपेटें।
<button onClick={() => handleDelete(item.id)}>Delete</button>
कारण 2: निर्भरता सारणी के बिना उपयोग प्रभाव
प्रत्येक रेंडर के बाद बिना किसी दूसरे तर्क के एक प्रभाव चलता है। यदि यह स्थिति सेट करता है, तो वह स्थिति परिवर्तन एक अन्य रेंडर को ट्रिगर करता है, जो प्रभाव को फिर से चलाता है।
function Profile({ userId }) {
const [user, setUser] = useState(null);
// ❌ No dependency array — runs after every render, forever.
useEffect(() => {
fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
});
}
// ✅ Re-runs only when userId changes.
useEffect(() => {
fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
}, [userId]);
किसी सरणी को छोड़ना किसी खाली सरणी को छोड़ने से अलग है। किसी भी सरणी का अर्थ “प्रत्येक रेंडर” नहीं है; [] का अर्थ है “एक बार पर्वत पर”।
कारण 3: एक निर्भरता जो हर रेंडर को बदल देती है
वस्तुओं, सरणियों और कार्यों की तुलना संदर्भ द्वारा की जाती है। प्रत्येक एक नए रेंडर का मतलब है कि निर्भरता हमेशा बदली हुई दिखती है।
function Search({ query }) {
const [results, setResults] = useState([]);
// New object identity on every render.
const options = { query, limit: 20 };
useEffect(() => {
search(options).then(setResults);
}, [options]); // ❌ always "changed" -> loop
}
आदिमों पर निर्भर रहें, जो मूल्य के आधार पर तुलना करते हैं।
useEffect(() => {
search({ query, limit: 20 }).then(setResults);
}, [query]); // ✅ string compares by value
जब वस्तु को प्रभाव के बाहर मौजूद होना चाहिए, तो उसे याद रखें।
const options = useMemo(() => ({ query, limit: 20 }), [query]);
useEffect(() => {
search(options).then(setResults);
}, [options]); // ✅ identity is stable until query changes
कारण 4: मान प्राप्त करने के लिए राज्य की स्थापना करना
मौजूदा मानों से किसी चीज़ की गणना करने के लिए राज्य प्लस प्रभाव का उपयोग करने से एक अनावश्यक चक्र और एक अतिरिक्त रेंडर बनता है।
function Cart({ items }) {
const [total, setTotal] = useState(0);
// ❌ Unnecessary, and loops if the dependency is wrong.
useEffect(() => {
setTotal(items.reduce((sum, i) => sum + i.price, 0));
});
return <p>Total: {total}</p>;
}
रेंडर के दौरान प्रॉप्स या स्टेट से गणना योग्य किसी भी चीज़ की गणना की जानी चाहिए।
function Cart({ items }) {
// ✅ No state, no effect, no possible loop.
const total = items.reduce((sum, i) => sum + i.price, 0);
return <p>Total: {total}</p>;
}
यदि गणना वास्तव में महंगी है, तो इसेuseMemoमें लपेटें – लेकिन फिर भी इसे राज्य में न रखें।
const total = useMemo(
() => items.reduce((sum, i) => sum + i.price, 0),
[items]
);
सशर्त-रेंडर संस्करण
रेंडर के दौरान कंडीशनल के अंदर स्थिति सेट करना छद्मवेश के समान ही बग है।
function Form({ initialValue }) {
const [value, setValue] = useState('');
// ❌ Still a state update during render.
if (initialValue && !value) {
setValue(initialValue);
}
}
इसके बजाय प्रारंभिक अवस्था ठीक से बनाएं। यदि प्रारंभिक मान की गणना करना महंगा है, तो एक फ़ंक्शन पास करें ताकि यह केवल एक बार चले।
const [value, setValue] = useState(initialValue ?? '');
// Lazy initialiser — the function runs on the first render only.
const [rows, setRows] = useState(() => parseLargeCsv(raw));
यदि प्रोप बदलने पर मान रीसेट होना चाहिए, तो मुहावरेदार समाधान एकkeyहै घटक पर, जो इसे ताज़ा स्थिति के साथ पुन: स्थापित करता है।
<Form key={userId} initialValue={user.name} />
अपराधी को ढूंढना
जब कारण स्पष्ट न हो, तो वहां लॉग करें जहां से रेंडर की उत्पत्ति हुई है।
function MyComponent(props) {
console.count('MyComponent render');
console.log('props:', props);
// ...
}
रिएक्ट DevTools प्रोफाइलर “रिकॉर्ड क्यों प्रत्येक घटक रेंडर किया गया” सक्षम के साथ आपको बताता है कि प्रत्येक पास पर कौन सा प्रोप या स्थिति बदल गई है। प्रभाव लूप के लिए, चक्र को सीधे देखने के लिए प्रभाव के अंदर और सेटर के अंदर लॉग इन करें।
useEffect(() => {
console.log('effect ran, deps:', { userId, options });
}, [userId, options]);
त्वरित संदर्भ
| लक्षण | संभावित कारण | ठीक करें |
|---|---|---|
| माउंट पर तुरंत त्रुटियाँ | रेंडर के दौरान हैंडलर को बुलाया गया | एक तीर फ़ंक्शन में लपेटें |
| डेटा लोड होने के बाद लूप्स | निर्भरता सारणी गायब होने का प्रभाव | जोड़ें[] या सही डिपो |
| सही डिप्स के बावजूद लूप्स | वस्तु या कार्य निर्भरता | आदिम का प्रयोग करें,useMemo, याuseCallback |
| प्रत्येक परिवर्तन पर अतिरिक्त रेंडर | व्युत्पन्न मूल्य राज्य में संग्रहीत | रेंडर के दौरान गणना करें |
| एक सशर्त पर लूप्स | रेंडर ब्रांच के अंदर सेटस्टेट | प्रारंभिक अवस्था या उपयोगkey |
अक्सर पूछे जाने वाले प्रश्न
प्रश्न: ऐसा कभी-कभी ही क्यों होता है?
उत्तर: डेटा पर निर्भर लूप्स को पहले डेटा की आवश्यकता होती है। बग प्रारंभ से ही मौजूद है, लेकिन केवल तभी ट्रिगर होता है जब फ़ेच का समाधान हो जाता है और स्थिति सेट हो जाती है।
प्रश्न: क्या रेंडर के दौरान स्थिति निर्धारित करना कभी स्वीकार्य है?
ए: रिएक्ट एक संकीर्ण मामले की अनुमति देता है – जब प्रोप बदलता है तो रेंडर के दौरान स्थिति को समायोजित करना, केवल उसी घटक को अपडेट करना, और तुलना द्वारा संरक्षित किया जाता है। यह शायद ही सबसे अच्छा उत्तर है; रेंडर के दौरान याkey.
का उपयोग करते समय कंप्यूटिंग को प्राथमिकता दें प्रश्न: क्या कक्षा घटकों में ऐसा होता है?
उत्तर: हाँ. कॉलिंगsetState in render या बिना शर्तcomponentDidUpdateमें समान लूप उत्पन्न करता है।
प्रश्न: मेरे प्रभाव में एक खाली सरणी है लेकिन फिर भी लूप है। क्यों?
उ: फिर लूप कहीं और है – अक्सर रेंडर के दौरान एक हैंडलर को बुलाया जाता है, या माता-पिता बच्चे को फिर से माउंट करते हैं क्योंकि यहkey हर रेंडर बदलता है।
प्रश्न: क्या रिएक्ट कंपाइलर मेरे लिए इसे ठीक करेगा?
उत्तर: यह स्वचालित रूप से पहचान को संभालकर समस्या की संस्मरण श्रेणी को हटा देता है। यह रेंडर के दौरान हैंडलर को कॉल करने को ठीक नहीं करता है, जो JSX में एक सीधी गलती है।
निष्कर्ष
“बहुत सारे री-रेंडर” का हमेशा मतलब होता है कि रेंडरिंग के हिस्से के रूप में स्थिति को अपडेट किया जा रहा है। क्रम से चार कारणों की जाँच करें:जेएसएक्स में पारित होने के बजाय एक हैंडलर को बुलाया गया, एuseEffectबिना किसी निर्भरता सरणी के, एक ऑब्जेक्ट या फ़ंक्शन निर्भरता जो हर रेंडर की पहचान बदलती है, और व्युत्पन्न मानों को गणना के बजाय राज्य में संग्रहीत किया जाता है। अंतर्निहित नियम सरल है – रेंडरिंग एक शुद्ध गणना होनी चाहिए, और प्रत्येक राज्य अपडेट एक इवेंट हैंडलर या प्रभाव से उत्पन्न होना चाहिए, कभी भी रेंडर से नहीं।
🔗 Share this article
✍️ Leave a Comment