الخطأتم تجاوز الحد الأقصى لعمق التحديث. يمكن أن يحدث هذا عندما يقوم أحد المكونات باستدعاء setState بشكل متكرر داخل مكونWillUpdate أو ComponentDidUpdate (أو في useEffect) يعني أن المكون الخاص بك عالق في حلقة عرض لا نهائية. هنا كل سبب وعلاجه.
📋 Table of Contents
- لماذا يحدث هذا
- السبب 1: استدعاء setState مباشرة في العرض/النص
- السبب 2: استدعاء دالة بدلاً من تمريرها
- السبب 3: استخدام التأثير لتحديث التبعية الخاصة به
- السبب 4: الكائن/الصفيف/الوظيفة قيد الاستخدام تبعيات التأثير
- السبب 5: تحديد الحالة في معالج الأحداث الذي يتم تشغيله عند العرض
- كيفية تصحيح أخطاء الحلقة
- القواعد الذهبية
- الأسئلة المتداولة
- الخلاصة
لماذا يحدث هذا
يتم إعادة عرض React عندما تتغير الحالة. إذا أدى تحديث الحالة إلى تشغيل عرض يؤدي إلى تحديث حالة آخر، فستحصل على حلقة لا نهائية. يكتشف React ذلك ويعرض رسالة “تم تجاوز الحد الأقصى لعمق التحديث” لمنع متصفحك من التجميد.
السبب 1: استدعاء setState مباشرة في العرض/النص
// 🐛 setState called during render → triggers re-render → loops forever
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // ❌ runs on every render → infinite loop
return <div>{count}</div>;
}
// ✅ Only update state in event handlers or effects
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
السبب 2: استدعاء دالة بدلاً من تمريرها
// 🐛 onClick={handleClick()} CALLS handleClick during render
<button onClick={handleClick()}>Click</button>
// If handleClick calls setState, it loops on every render
// ✅ Pass the function reference, don't call it
<button onClick={handleClick}>Click</button>
// ✅ Or wrap in an arrow function if you need to pass arguments
<button onClick={() => handleClick(id)}>Click</button>
السبب 3: استخدام التأثير لتحديث التبعية الخاصة به
// 🐛 Effect sets state that's in its dependency array
function Component() {
const [data, setData] = useState([]);
useEffect(() => {
setData([...data, newItem]); // ❌ changes data → effect re-runs → loop
}, [data]); // depends on data, which the effect changes
}
// ✅ Use a functional update and remove data from deps
function Component() {
const [data, setData] = useState([]);
useEffect(() => {
setData(prev => [...prev, newItem]); // functional update
}, []); // runs once — no dependency on data
}
السبب 4: الكائن/الصفيف/الوظيفة قيد الاستخدام تبعيات التأثير
// 🐛 New object reference every render → effect always re-runs
function Component({ userId }) {
const options = { id: userId }; // NEW object each render
useEffect(() => {
fetchData(options);
setState(...);
}, [options]); // options is "new" every render → infinite loop
}
// ✅ Depend on the primitive value, or memoize the object
function Component({ userId }) {
useEffect(() => {
fetchData({ id: userId });
setState(...);
}, [userId]); // primitive — stable across renders
// OR memoize:
const options = useMemo(() => ({ id: userId }), [userId]);
}
السبب 5: تحديد الحالة في معالج الأحداث الذي يتم تشغيله عند العرض
// 🐛 onChange fires and setState causes re-render, which re-triggers...
// often from passing changing props to controlled components incorrectly
// 🐛 Also common: updating parent state that re-renders and resets child
function Parent() {
const [value, setValue] = useState('');
return <Child onValueChange={setValue(newValue)} />; // ❌ called in render
}
// ✅ Pass the setter reference
function Parent() {
const [value, setValue] = useState('');
return <Child onValueChange={setValue} />; // pass reference
}
كيفية تصحيح أخطاء الحلقة
// 1. Add a log to see how often the component renders
function Component() {
console.log('render'); // if this floods the console, you have a loop
// ...
}
// 2. Log inside the effect to find the culprit
useEffect(() => {
console.log('effect running');
// ...
}, [dep1, dep2]);
// 3. Log each dependency to see which one keeps changing
useEffect(() => {
console.log('dep1:', dep1, 'dep2:', dep2);
}, [dep1, dep2]);
// 4. The React DevTools Profiler highlights components re-rendering rapidly
القواعد الذهبية
- لا تستدعي أبدًا setState مباشرة في نص المكون – فقط في معالجات الأحداث أو التأثيرات أو عمليات الاسترجاعات
- تمرير مراجع الوظائف إلى معالجات الأحداث، لا تتصل بهم (
onClick={fn}لاonClick={fn()}) - استخدم التحديثات الوظيفية (
setState(prev => ...)) عندما تعتمد الحالة الجديدة على الحالة القديمة - تعتمد على البدائيات في useEffect، أو حفظ الكائنات/الوظائف باستخدام useMemo/useCallback
- لا تضع الحالة في عمليات استخدام التأثير إذا كان التأثير يقوم بتحديث تلك الحالة
الأسئلة المتداولة
س: ما الفرق بين هذا و”عدد كبير جدًا من عمليات إعادة العرض”؟
ج: إنهما مرتبطان ارتباطًا وثيقًا، وكلاهما عبارة عن حلقات عرض لا نهائية. “عدد كبير جدًا من عمليات إعادة العرض” يعني عادةً أنه يتم استدعاء setState مباشرة أثناء العرض؛ غالبًا ما يشير “تجاوز الحد الأقصى لعمق التحديث” إلى تأثير useEffect أو أسلوب دورة الحياة الذي يتسبب في حدوث الحلقة. تتداخل الإصلاحات.
س: لماذا يؤدي تمرير setState مباشرة إلى حدوث حلقة؟
ج: إذا كتبتonClick={setState(x)}، يستدعي setState أثناء العرض (وليس عند النقر). يقوم setState بتشغيل عملية إعادة التصيير، والتي تستدعيها مرة أخرى – حلقة لا نهائية. تمريرonClick={() => setState(x)} بدلاً من.
س: كيف يمكنني تحديث الحالة بناءً على الحالة السابقة بأمان؟
ج: استخدم التحديث الوظيفي:setCount(prev => prev + 1). هذا لا يتطلبcount في مصفوفة التبعية الخاصة بك، لتجنب سبب الحلقة الشائعة.
س: تستمر تبعية الكائن في التسبب في حدوث حلقات. ماذا أفعل؟
ج: تحصل الكائنات والمصفوفات على مراجع جديدة في كل عرض. إما أن تعتمد على قيم أولية بداخلها ([obj.id]not[obj])، أو تحفظ الكائن باستخدامuseMemo(() => obj, [deps]).
س: هل يستطيع ESLint التقاط هذه الأشياء؟
ج:eslint-plugin-react-hooks تشير قاعدة deps الشاملة إلى العديد من مشكلات التبعية. لن يلتقط كل حلقة، لكنه يمنع أخطاء تبعية useEffect الأكثر شيوعًا. تثبيته وتمكينه.
الخلاصة
“تم تجاوز الحد الأقصى لعمق التحديث” دائمًا عبارة عن حلقة عرض لا نهائية. الأسباب الأكثر شيوعًا: استدعاء setState مباشرةً في نص العرض، أو استدعاء دالة في معالج الأحداث (بدلاً من تمريرها)، أو useEffect الذي يقوم بتحديث تبعياته، أو تبعيات الكائن/الوظيفة غير المستقرة. الإصلاحات:تحديث الحالة فقط في المعالجات والتأثيرات، وتمرير مراجع الوظائف، واستخدام التحديثات الوظيفية، والاعتماد على الأوليات أو القيم المحفوظة. أضفconsole.log('render') لتأكيد الحلقة، ثم قم بتسجيل كل تبعية للعثور على أي منها يستمر في التغيير – عادةً ما يكون السبب واضحًا بمجرد رؤيته.
🔗 Share this article
✍️ Leave a Comment