الخطأيتم استدعاء خطاف التفاعل “useState” بشكل مشروط. يجب استدعاء خطافات React بنفس الترتيب في كل تصيير مكون يعد أحد أكثر أخطاء React شيوعًا. إنه ينتهك قواعد الخطافات. وإليك سبب حدوث ذلك وكيفية إصلاح كل متغير.
📋 Table of Contents
لماذا توجد هذه القاعدة
React يتتبع الخطافات من خلالطلب الاتصال، وليس بالاسم. في كل تصيير، تتوقع React نفس الخطافات بنفس التسلسل. إذا كان الخطاف داخل شرط ما، فقد يتم تشغيله في تصيير واحد ولكن ليس في التصيير التالي – مما يؤدي إلى كسر التتبع الداخلي لـ React والتسبب في تعيين الحالة إلى الخطاف الخطأ.
الكود المكسور
// 🐛 Hook inside a condition — VIOLATES rules of hooks
function Profile({ userId }) {
if (!userId) {
return <div>No user</div>; // early return BEFORE hooks
}
const [user, setUser] = useState(null); // ❌ conditionally called
// If userId is falsy, this hook is skipped — breaks React's tracking
return <div>{user?.name}</div>;
}
الإصلاح 1: استدعاء الخطافات أولاً، والحالة لاحقًا
// ✅ All hooks at the top, unconditionally
function Profile({ userId }) {
const [user, setUser] = useState(null); // always called
// Conditional logic AFTER all hooks
if (!userId) {
return <div>No user</div>;
}
return <div>{user?.name}</div>;
}
الإصلاح 2: ربط داخل كتلة الشرط
// 🐛 useEffect inside an if
function Component({ isActive }) {
if (isActive) {
useEffect(() => { // ❌ conditional hook
console.log('active');
}, []);
}
}
// ✅ Move the condition INSIDE the hook
function Component({ isActive }) {
useEffect(() => {
if (isActive) { // condition inside the hook
console.log('active');
}
}, [isActive]);
}
الإصلاح 3: ربط داخل حلقة
// 🐛 Hook in a loop — order changes if items.length changes
function List({ items }) {
return items.map(item => {
const [selected, setSelected] = useState(false); // ❌ in a loop
return <Item key={item.id} />;
});
}
// ✅ Extract into a child component — each has its own hooks
function List({ items }) {
return items.map(item => <Item key={item.id} item={item} />);
}
function Item({ item }) {
const [selected, setSelected] = useState(false); // ✅ top level of component
return <div onClick={() => setSelected(!selected)}>{item.name}</div>;
}
الإصلاح 4: ربط بعد العودة المبكرة
// 🐛 Hook after a conditional return
function Widget({ data }) {
const [count, setCount] = useState(0);
if (!data) return null; // early return
const [name, setName] = useState(''); // ❌ never reached when !data
return <div>{name}: {count}</div>;
}
// ✅ ALL hooks before ANY conditional return
function Widget({ data }) {
const [count, setCount] = useState(0);
const [name, setName] = useState(''); // moved up
if (!data) return null; // early return AFTER all hooks
return <div>{name}: {count}</div>;
}
قاعدتا الخطافات
- استدعاء الخطافات فقط في المستوى الأعلى – لا تدخل أبدًا في الشروط أو الحلقات أو الوظائف المتداخلة. دائما بنفس الترتيب.
- استدعاء الخطافات فقط من وظائف React – مكونات التفاعل أو الخطافات المخصصة (الوظائف التي تبدأ بـ
use). ليس من وظائف JavaScript العادية.
التعامل مع المنطق الشرطي بشكل صحيح
function Dashboard({ user, showStats }) {
// ✅ Always call all hooks unconditionally
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Conditions go INSIDE the hook, not around it
if (showStats) {
fetchStats().then(setData).finally(() => setLoading(false));
} else {
setLoading(false);
}
}, [showStats]);
// Conditional RENDERING is fine — it's not a conditional hook
if (!user) return <Login />;
if (loading) return <Spinner />;
return showStats ? <Stats data={data} /> : <Overview />;
}
فرض القواعد باستخدام ESLint
npm install --save-dev eslint-plugin-react-hooks
// .eslintrc — catches these errors before runtime
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
الأسئلة المتداولة
س: لماذا لا تكون الخطافات مشروطة؟ يبدو تعسفيا.
ج: تتعرّف React على الخطافات حسب ترتيب الاتصال الخاص بها (useState الأول، useState الثاني، وما إلى ذلك)، وليس بالاسم. إذا تم تخطي الخطاف في بعض الأحيان، يتغير الترتيب وتقوم React بتعيين الحالة للخطاف الخطأ. النظام المتسق هو ما يجعل الخطافات تعمل.
س: هل يمكنني استخدام الشروط داخل الخطاف؟
ج: نعم! الشروط الموجودة داخل الخطاف (داخل رد اتصال useEffect، على سبيل المثال) جيدة. تحظر القاعدة فقط استدعاء الخطاف نفسه بشكل مشروط.
س: كيف يمكنني تشغيل التأثير بشكل مشروط؟
ج: ضع الشرط داخل نص التأثير وقم بتضمين متغير الشرط في مصفوفة التبعية:useEffect(() => { if (cond) {...} }, [cond]).
س: ماذا عن العرض الشرطي، هل هذا مسموح به؟
ج: نعم. يعد العرض الشرطي (إرجاع JSX مختلفًا بناءً على الحالة) أمرًا جيدًا تمامًا، طالما تم استدعاء جميع الخطافات قبل أي إرجاع مشروط. شروط العرض ليست شروط ربط.
س: هل يمكنني استدعاء خطاف مخصص بشكل مشروط؟
ج: لا – تتبع الخطافات المخصصة نفس القواعد لأنها تستدعي الخطافات داخليًا. اتصل بهم دون قيد أو شرط في المستوى الأعلى، وضع الشروط داخلهم.
الخلاصة
الخطأ “استدعاء خطاف التفاعل المشروط” يعني أن الخطاف موجود داخل شرط أو حلقة أو بعد إرجاع مبكر. الإصلاح هو نفسه دائمًا:قم باستدعاء جميع الخطافات دون قيد أو شرط في الجزء العلوي من المكون الخاص بك، قبل أي عوائد شرطية أو منطق. ضع الشروط داخل الخطافات (في جسم التأثير)، وليس حولها. تثبيتeslint-plugin-react-hooks لاكتشاف هذه الأخطاء تلقائيًا قبل أن تصل إلى وقت التشغيل – إنها أفضل وسيلة حماية ضد انتهاكات قاعدة الربط.
🔗 Share this article
✍️ Leave a Comment