في هذا الدرس 6تصفّح المواضيع
لا تضمن الشبكات وصول ردود بروتوكول HTTP بنفس ترتيب إرسالها دائماً. تخيل أن المستخدم نقر على تبويب Frontend فاستغرق الخادم 500ms، ثم نقر فوراً على تبويب Hooks فوصل رده الخفيف خلال 120ms فقط. في غياب دالة تنظيف، ستظهر دورات Hooks أولاً، ثم يصل الرد القديم المتأخر بعد أجزاء من الثانية ويستدعي setCourses() فيطمس الشاشة ببيانات التبويب السابق بالخطأ!
يُعرف هذا الخلل الزمني الشهير باسم تضارب السباق في الشبكة (Race Condition)؛ لأن طلبين متوازيين يتسابقان لتحديث نفس الحالة دون مراعاة أيّهما يمثل اختيار المستخدم الأخير.
أبسط دفاع قياسي يوصي به التوثيق الرسمي لمكتبة React ضد تضارب الردود هو استخدام راية منطقية محلية داخل useEffect(): نعرّف let ignore = false في مطلع التأثير، ونشترط if (!ignore) قبل أي تحديث للحالة، ثم نعيد دالة التنظيف () => { ignore = true; }.
نظراً لأن كل دورة تصيير تمتلك نطاقاً مغلقاً خاصاً بها (Closure)، فإن تغيير التصنيف يضبط راية الطلب القديم على true بينما يبدأ الطلب الجديد براية جديدة false، فتُهمل أي نتيجة متأخرة بأمان تام.
useEffect(() => {
let ignore = false;
async function load() {
const url = `/api/courses?category=${cat}`;
const res = await fetch(url);
const data = await res.json();
if (!ignore) setCourses(data);
}
load();
return () => { ignore = true; };
}, [cat]);
تحمي راية ignore حالة المكون من التلوث، لكن المتصفح يظل مستمراً في تنزيل بقية حزمة الشبكة المهملة في الخلفية. لإلغاء اتصال الشبكة الفعلي ذاته، أنشئ كائن new AbortController() القياسي داخل useEffect()، ومرّر الإشارة { signal: controller.signal } ضمن خيارات fetch()، ثم استدعِ controller.abort() داخل دالة التنظيف.
يوفر الإلغاء الفعلي للطلبات الملغاة استهلاك البيانات ويحرر قنوات الاتصال في المتصفح فوراً، وهو أمر بالغ الأهمية في القوائم الكبيرة والبحث الفوري.
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((r) => r.json())
.then(setCourses)
.catch((e) => {
if (e.name !== 'AbortError') setStatus('error');
});
return () => controller.abort();
}, [url]);
عندما تلغي الدالة controller.abort() طلب fetch() قيد التنفيذ، يرفض المتصفح الوعد باستثناء قياسي خاص يحمل الاسم err.name === 'AbortError'. فإذا تعاملت كتلة catch مع كافة الأخطاء بالطريقة نفسها، فسيظهر للمستخدم تنبيه فشل أحمر زائف بمجرد تنقله السريع بين التبويبات!
افحص دائماً الشرط if (err.name === 'AbortError') return; في أول سطر من catch. وإذا استخدمت كتلة finally لإيقاف مؤشر التحميل، فقيّدها بالشرط if (!controller.signal.aborted) حتى لا يوقف الطلب الملغى مؤشر التحميل الخاص بالطلب الأحدث الجاري حالياً.
تُطلق حقول البحث الفوري حدث التغيير مع كل حرف يكتبه المستخدم. يتيح دمج مؤقت انتظار قصير عبر setTimeout() (مثل 250ms) مع AbortController داخل دالة تنظيف واحدة تجربة بحث فائقة السلاسة دون إغراق الخادم بطلبات غير مكتملة:
- إذا كتب المستخدم حرفاً تالياً قبل مرور
250ms، تلغيclearTimeout(timerId)الطلب المجدول قبل انطلاقه أصلاً. - وإذا كان هناك طلب سابق قد انطلق بالفعل، تتكفل
controller.abort()بإلغائه فوراً.
useEffect(() => {
const ctrl = new AbortController();
const id = setTimeout(() => {
const opts = { signal: ctrl.signal };
fetch(`/api/courses?q=${q}`, opts)
.then((r) => r.json())
.then(setResults)
.catch(() => {});
}, 250);
return () => { clearTimeout(id); ctrl.abort(); };
}, [q]);
إن الجمع بين مصفوفة الاعتماديات الدقيقة، وإلغاء الاتصال عبر AbortController، واستثناء AbortError المتوقع يقضي جذرياً على تلوث الحالة في التبويبات والفلاتر. يلخص الجدول التالي مستويات الحماية الأربعة:
| أسلوب الجلب | هل يمنع تلوث الحالة؟ | الأثر على استهلاك الشبكة |
|---|---|---|
| جلب بدون دالة تنظيف | لا — الردود البطيئة تطمس التبويبات الأحدث | يستكمل تنزيل جميع الردود المهملة بالكامل |
| راية التجاهل المنطقية ignore | نعم — يتخطى تحديث الحالة للطلب المتقادم | يستمر تنزيل الرد في الخلفية دون عرضه |
| أداة AbortController مع signal | نعم — يوقف المسار القديم ويتجاهل نتيجته | يقطع طلب الشبكة غير اللازم فوراً |
| مؤقت تأجيل + AbortController | نعم — يحصّن حقول البحث السريعة المتلاحقة | يختصر الأحرف البينية ويلغي الطلبات البطيئة |
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
تعرفت في هذا الدرس على أسباب تضارب ردود الشبكة المتأخرة في React وكيفية القضاء عليها عبر راية التجاهل في دالة التنظيف، وإلغاء الطلبات بـ AbortController، واستثناء AbortError المتوقع، وتهدئة طلبات البحث المتلاحقة. الخطوة التالية: انتقل الآن إلى الدرس رقم 40 «إرسال وتحديث وحذف البيانات عبر طلبات HTTP في React» لمواصلة البناء التراكمي المتدرج في مسار React الحديث.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 Race Condition تضارب السباق الزمني
- خلل ينشأ عندما يصل رد طلب قديم بطيء بعد اكتمال طلب أحدث منه فيطمس البيانات الصحيحة في الواجهة.
- للمراجعة
- 02 AbortController متحكم إلغاء الطلبات
- أداة متصفح قياسية تولّد إشارة AbortSignal قادرة على قطع طلبات fetch الجارية فوراً عند انتفاء الحاجة إليها.
- للمراجعة
- 03 AbortError استثناء الإلغاء المقصود
- اسم الاستثناء الذي يرميه المتصفح عند إلغاء الطلب عمداً، ويجب تجاهله في catch بدلاً من عرضه كعطل.
- للمراجعة
- 04 Closure Flag راية النطاق المغلق
- متغير منطقي محلي خاص بدورة تشغيل واحدة للتأثير يتحول إلى true عند التنظيف لمنع تحديث الحالة المتأخر.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 FIFO FIFO
- First In, First Out
- ترتيب الوصول المتسلسل الذي لا تضمنه الشبكات عند إرسال عدة طلبات غير متزامنة متتالية.
- للمراجعة
- 02 DOM DOM
- Document Object Model
- مواصفة المتصفح القياسية التي تعرّف كائنات الصفحة والاستثناءات القياسية مثل DOMException.
- للمراجعة
- 03 HTTP HTTP
- Hypertext Transfer Protocol
- بروتوكول نقل البيانات الذي يتم قطع اتصاله الجاري فور استدعاء دالة controller.abort().
- للمراجعة
- 04 UX UX
- User Experience
- تجربة المستخدم التي تبقى متسقة وخالية من ومضات التحميل أو تداخل بيانات التبويبات.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
افتح مثال SafeCategoryTabs في محرر التجريب وانقر frontend ثم hooks بسرعة وتأكد من بقاء دورات hooks الصحيحة على الشاشة.
-
افتح مثال AbortableCourseViewer في محرر التجريب وبدّل بين الأزرار بسرعة لملاحظة ارتفاع عداد الطلبات الملغاة بنظافة.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي10 أسئلة تنتظرك +
كيف يفسد تضارب السباق الزمني واجهة مكون React عند التبديل السريع بين التبويبات؟
يصل رد الطلب الأقدم البطيء متأخراً بعد اكتمال الطلب الأحدث فيستبدل الحالة ببيانات قديمة خاطئة.
تعريف let ignore = false داخل التأثير وضبطها على true في دالة التنظيف يمنع الردود المتقادمة من تحديث الحالة.
صواب؛ لأن كل دورة تأثير تحتفظ بمتغير ignore مستقل في نطاقها المغلق.
كيف تربط كائن AbortController بطلب الشبكة القياسي fetch()؟
بتمرير الكائن { signal: controller.signal } في المعامل الثاني لدالة fetch.
أين يجب استدعاء controller.abort() عند جلب البيانات داخل خطاف useEffect؟
داخل دالة التنظيف المُعادة من useEffect لتعمل فور تغير المدخلات أو إزالة المكون.
يجب إنشاء كائن new AbortController() جديد في كل تشغيل لـ useEffect لأن الإشارة الملغاة تبقى ملغاة دائماً.
صواب؛ فإعادة استخدام إشارة ملغاة ترفض أي طلب جديد على الفور.
ما قيمة الخاصية err.name للاستثناء الناتج عن إلغاء طلب fetch بواسطة controller.abort()؟
يرفض المتصفح الطلب الملغى باستثناء قياسي يحمل الاسم النصي "AbortError".
لماذا يجب الخروج بـ return دون تفعيل حالة الخطأ عندما يكون err.name مساوياً لـ "AbortError"؟
لأن الإلغاء تم عمداً لتنظيف طلب قديم وليس بسبب عطل حقيقي في الشبكة.
حماية إيقاف التحميل داخل finally بالشرط !controller.signal.aborted تمنع الطلب الملغى من إخفاء مؤشر التحميل للطلب الجديد.
صواب؛ لأن finally تعمل حتى عند الإلغاء، فلا نعدل الحالة إلا إذا لم تكن الإشارة ملغاة.
عند دمج مؤقت setTimeout مع AbortController للبحث الحي، ماذا يجب أن تنفذ دالة التنظيف؟
تستدعي كلاً من clearTimeout لإلغاء المؤقت المنتظر وcontroller.abort() لإلغاء أي طلب جارٍ.
ما الميزة الإضافية التي تقدمها أداة AbortController مقارنة براية التجاهل المنطقية ignore وحدها؟
تقطع اتصال الشبكة الفعلي في المتصفح فتصون استهلاك البيانات بدلاً من الاكتفاء بإهمال النتيجة بعد تنزيلها.