Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.প্রতিক্রিয়া আপনাকে বলছে যে রেন্ডারিং একটি স্টেট আপডেট করেছে, যার ফলে একটি রেন্ডার হয়েছে, যার ফলে একটি আপডেট হয়েছে। কারণটি প্রায় সবসময়ই চারটি প্যাটার্নের মধ্যে একটি, এবং প্রতিটিরই একটি স্পষ্ট সমাধান রয়েছে।
📋 Table of Contents
- আসলে কি ঘটছে
- কারণ 1: এটি পাস করার পরিবর্তে হ্যান্ডলারকে কল করা
- কারণ 2: নির্ভরতা অ্যারে ছাড়াই ইফেক্ট ব্যবহার করুন
- কারণ 3: একটি নির্ভরতা যা প্রতিটি রেন্ডারকে পরিবর্তন করে
- কারণ 4: একটি মান অর্জনের জন্য রাজ্য নির্ধারণ করা
- শর্তসাপেক্ষ-রেন্ডার ভেরিয়েন্ট
- অপরাধীর সন্ধান
- দ্রুত রেফারেন্স
- প্রায়শই জিজ্ঞাসিত প্রশ্ন
- উপসংহার
আসলে কি ঘটছে
রেন্ডারিং প্রপস এবং স্টেট থেকে UI এর একটি বিশুদ্ধ হিসাব হতে হবে। যদি এটি রাজ্যও সেট করে, আপনি একটি চক্র তৈরি করেন। রিঅ্যাক্ট পলাতককে শনাক্ত করে এবং ট্যাবটিকে ফ্রিজ করতে না দিয়ে এটি বন্ধ করে দেয়।
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 Profiler-এর সাথে “প্রতিটি কম্পোনেন্ট রেন্ডার করা কেন রেকর্ড করুন” সক্ষম করা আপনাকে বলে যে প্রতিটি পাসে কোন প্রপ বা অবস্থা পরিবর্তিত হয়েছে। প্রভাব লুপগুলির জন্য, চক্রটি সরাসরি দেখতে প্রভাবের ভিতরে এবং সেটারের ভিতরে লগ করুন।
useEffect(() => {
console.log('effect ran, deps:', { userId, options });
}, [userId, options]);
দ্রুত রেফারেন্স
| উপসর্গ | সম্ভবত কারণ | ঠিক করুন |
|---|---|---|
| মাউন্টে অবিলম্বে ত্রুটি | রেন্ডারের সময় হ্যান্ডলারকে ডাকা হয় | একটি তীর ফাংশনে মোড়ানো |
| ডেটা লোড হওয়ার পরে লুপস | প্রভাব একটি নির্ভরতা অ্যারে অনুপস্থিত | যোগ করুন[] অথবা সঠিক ডিপস |
| সঠিক ডিপস সত্ত্বেও লুপস | বস্তু বা ফাংশন নির্ভরতা | আদিম ব্যবহার করুন,useMemo, অথবাuseCallback |
| প্রতিটি পরিবর্তনে অতিরিক্ত রেন্ডার | প্রাপ্ত মান রাজ্যে সংরক্ষিত | রেন্ডারের সময় গণনা করুন |
| শর্তসাপেক্ষে লুপস | রেন্ডার শাখার ভিতরে setState | রাজ্য শুরু করুন বা ব্যবহার করুনkey |
প্রায়শই জিজ্ঞাসিত প্রশ্ন
প্রশ্ন: কেন এটি শুধুমাত্র মাঝে মাঝে ঘটে?
উত্তর: ডেটার উপর নির্ভরশীল লুপগুলির জন্য প্রথমে ডেটা পৌঁছাতে হবে। বাগটি শুরু থেকেই উপস্থিত থাকে কিন্তু একটি নিয়ে আসা সমাধান এবং স্থিতি সেট করার পরেই ট্রিগার হয়৷
প্রশ্নঃ রেন্ডারের সময় কি কখনো স্টেট সেট করা গ্রহণযোগ্য?
উত্তর: প্রতিক্রিয়া একটি সংকীর্ণ ক্ষেত্রে অনুমতি দেয় — যখন একটি প্রপ পরিবর্তন হয় তখন রেন্ডারের সময় অবস্থা সামঞ্জস্য করা, শুধুমাত্র একই উপাদান আপডেট করা, এবং একটি তুলনা দ্বারা সুরক্ষিত। এটি খুব কমই সেরা উত্তর; রেন্ডার বা ব্যবহার করার সময় কম্পিউটিং পছন্দ করুনkey.
প্রশ্নঃ এটা কি ক্লাস কম্পোনেন্টে ঘটে?
উঃ হ্যাঁ। কলিংsetState in render অথবা নিঃশর্তভাবেcomponentDidUpdate অভিন্ন লুপ তৈরি করে।
প্রশ্ন: আমার প্রভাব একটি খালি অ্যারে আছে কিন্তু এখনও loops. কেন?
উত্তর: তারপর লুপটি অন্য জায়গায় থাকে — প্রায়শই রেন্ডারের সময় একজন হ্যান্ডলারকে আমন্ত্রণ জানানো হয়, অথবা একজন অভিভাবক সন্তানকে পুনরায় মাউন্ট করছেন কারণ এটিkey প্রতিটি রেন্ডার পরিবর্তন করে।
প্রশ্ন: প্রতিক্রিয়া কম্পাইলার কি আমার জন্য এটি ঠিক করবে?
উত্তর: এটি স্বয়ংক্রিয়ভাবে পরিচয় পরিচালনা করে সমস্যার মেমোইজেশন বিভাগকে সরিয়ে দেয়। এটি রেন্ডারের সময় হ্যান্ডলারকে কল করা ঠিক করে না, যা JSX-এ একটি সহজবোধ্য ভুল।
উপসংহার
“অনেকগুলো রি-রেন্ডার” মানে সবসময় রেন্ডারিংয়ের অংশ হিসেবে স্টেট আপডেট করা হচ্ছে। ক্রমানুসারে চারটি কারণ পরীক্ষা করুন:জেএসএক্সে পাস করার পরিবর্তে একটি হ্যান্ডলারকে ডাকা হয়, কuseEffectকোন নির্ভরতা অ্যারে ছাড়াই, একটি বস্তু বা ফাংশন নির্ভরতা যা প্রতিটি রেন্ডারে পরিচয় পরিবর্তন করে, এবং গণনার পরিবর্তে স্ট্যাটে সংরক্ষিত প্রাপ্ত মান। অন্তর্নিহিত নিয়মটি সহজ – রেন্ডারিং অবশ্যই একটি বিশুদ্ধ গণনা হতে হবে এবং প্রতিটি স্টেট আপডেট অবশ্যই একটি ইভেন্ট হ্যান্ডলার বা প্রভাব থেকে উদ্ভূত হতে হবে, রেন্ডার থেকে নয়।
🔗 Share this article
✍️ Leave a Comment