في هذا الدرس 6تصفّح المواضيع
درست سابقاً الفارق المعماري الجوهري بين التأثيرات ومعالجات الأحداث: فخطاف useEffect() يزامن البيانات لمجرد ظهور المكون على الشاشة، أما عمليات تعديل البيانات مثل إنشاء سجل بـ POST أو تحديثه بـ PUT أو حذفه بـ DELETE فتنطلق استجابةً لفعل صريح ومقصود من المستخدم عند إرسال نموذج أو نقر زر.
نفّذ جميع طلبات الكتابة والتعديل داخل دوال أحداث غير متزامنة مثل async function handleSubmit(e)، ولا تضعها داخل useEffect() أبداً حتى لا تتكرر عمليات الإرسال تلقائياً أثناء التطوير أو عند تغير حالة جانبية.
لإرسال بيانات نموذج بصيغة JSON عبر طلب POST، ابدأ باستدعاء e.preventDefault() لمنع المتصفح من إعادة تحميل الصفحة. ثم مرّر كائن الإعدادات إلى fetch() متضمناً method: 'POST'، وتحديد نوع المحتوى في رؤوس HTTP عبر headers: { 'Content-Type': 'application/json' }، وتحويل الكائن لنص عبر body: JSON.stringify(payload).
وبعد التحقق من نجاح response.ok، اقرأ السجل العائد من الخادم (الذي يحمل المعرّف الرسمي المولّد id) وأضفه بنقاء إلى مصفوفة الحالة عبر التحديث الدالي setTasks((prev) => [...prev, createdTask]).
const res = await fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, done: false }),
});
if (!res.ok) throw new Error('Create failed');
const created = await res.json();
setTasks((prev) => [...prev, created]);
لتعديل سجل قائم نرسل طلب method: 'PUT' أو 'PATCH' إلى عنوان المورد المحدد مثل /api/tasks/t-1، وفور نجاح الاستجابة نستبدل الكائن المعدّل داخل مصفوفة الحالة بنقاء عبر الدالة prev.map((item) => item.id === updated.id ? updated : item).
ولحذف سجل نرسل طلب method: 'DELETE' إلى عنوانه، وبعد التحقق من response.ok نستبعده من الواجهة عبر prev.filter((item) => item.id !== id).
// Update with PUT + map:
setTasks((prev) =>
prev.map((t) => (t.id === updated.id ? updated : t))
);
// Delete with DELETE + filter:
setTasks((prev) => prev.filter((t) => t.id !== id));
في التفاعلات السريعة كتبديل حالة إنجاز مهمة أو حفظ عنصر في المفضلة، قد يبدو انتظار استجابة الشبكة بطيئاً. يعتمد نمط التحديث المتفائل (Optimistic UI) على تحديث الحالة المحلية في الواجهة فوراً، مع الاحتفاظ المسبق بنسخة المرجع الأصلي const previousTasks = tasks.
فإذا فشل الطلب في الخلفية أو رد الخادم بخطأ، نستعيد النسخة السابقة فوراً عبر setTasks(previousTasks) ونعرض تنبيهاً صادقاً يوضح تعذر الحفظ الفعلي.
const snapshot = tasks;
setTasks((prev) =>
prev.map((t) =>
t.id === id ? { ...t, done: !t.done } : t
)
);
const res = await fetch(url, { method: 'PUT', body });
if (!res.ok) {
setTasks(snapshot);
setRollbackNotice('Save failed — reverted!');
}
تتطلب طلبات التعديل المحمية إثبات هوية المستخدم للخادم. تجنب تخزين رموز الجلسات الحساسة أو مفاتيح المصادقة طويلة الأمد داخل window.localStorage؛ لأن أي كود JavaScript يعمل في الصفحة يستطيع قراءته، مما يجعل أي ثغرة برمجة نصية عابرة للمواقع XSS كفيلة بتسريب جلسات المستخدمين.
المعيار الأمني الأرقى هو اعتماد كوكيز الجلسة المزودة بسمات HttpOnly; Secure; SameSite مع حماية CSRF؛ إذ يرسلها المتصفح تلقائياً مع credentials: 'same-origin' بينما تظل محجوبة تماماً عن قراءة السكريبتات. يلخص الجدول التالي قواعد عمليات التعديل:
| عملية الشبكة | إعداد دالة fetch<code>() | مزامنة مصفوفة الحالة في React |
|---|---|---|
| إنشاء سجل جديد | method: 'POST' مع جسم JSON | إلحاق السجل العائد من الخادم عبر [...prev, created] |
| تحديث سجل قائم | method: 'PUT' أو 'PATCH' مع جسم JSON | استبدال السجل المطابق بنقاء عبر prev.map<code>() |
| حذف سجل محدد | method: 'DELETE' على عنوان المورد | استبعاد المعرّف المحذوف عبر prev.filter<code>() |
| توثيق الجلسة | credentials: 'same-origin' مع كوكيز HttpOnly | حفظ تفضيلات العرض فقط ومنع تخزين الرموز السرية في localStorage |
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
أتقنت في هذا الدرس إرسال طلبات POST وPUT وDELETE من دوال الأحداث في React، ومزامنة الحالة بنقاء عبر map وfilter، ومنع الإرسال المكرر براية isSubmitting، وتطبيق التحديث المتفائل مع التراجع الآمن، وحماية جلسات المصادقة بكوكيز HttpOnly. الخطوة التالية: انتقل الآن إلى الدرس رقم 41 «بناء الخطافات المخصصة Custom Hooks لإعادة استخدام المنطق» لمواصلة البناء التراكمي المتدرج في مسار React الحديث.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 HTTP Mutation طلب تعديل البيانات
- طلب شبكة يغيّر البيانات المخزنة على الخادم مثل الإنشاء أو التحديث أو الحذف استجابة لفعل المستخدم.
- للمراجعة
- 02 Optimistic UI التحديث المتفائل للواجهة
- تحديث الواجهة فورياً قبل اكتمال رد الخادم مع الاحتفاظ بنسخة سابقة لاسترجاعها تلقائياً إذا فشل الحفظ.
- للمراجعة
- 03 Pending Guard حارس قيد الإرسال
- متغير حالة مثل isSubmitting يعطّل زر الحفظ ويمنع تكرار إرسال النموذج أثناء انتظار استجابة الخادم.
- للمراجعة
- 04 HttpOnly Cookie كوكيز محمية HttpOnly
- سمة أمنية تجعل المتصفح يرسل ملف الارتباط تلقائياً مع الطلبات ويحجبه تماماً عن قراءة أكواد JavaScript.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 CRUD CRUD
- Create, Read, Update, Delete
- العمليات الأربع الأساسية لإدارة البيانات الدائمة والمرتبطة بأفعال POST وGET وPUT وDELETE.
- للمراجعة
- 02 XSS XSS
- Cross-Site Scripting
- ثغرة حقن برمجيات نصية تمكّن المهاجم من قراءة أي رموز سرية مخزنة بشكل مكشوف في localStorage.
- للمراجعة
- 03 CSRF CSRF
- Cross-Site Request Forgery
- تزوير الطلبات عبر المواقع والذي يُصدّ بسياسات SameSite ورموز التحقق الأمنية الخاصة بالجلسة.
- للمراجعة
- 04 JWT JWT
- JSON Web Token
- صيغة رموز مصادقة موقّعة رقمياً تتطلب حماية صارمة من الوصول المباشر عبر السكريبتات.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
افتح مثال TaskCreator في محرر التجريب وأضف مهمتين جديدتين وراقب تحول زر الإضافة إلى Saving... أثناء الإرسال.
-
افتح مثال OptimisticChecklist في محرر التجريب وجرّب تبديل مهمة مع تفعيل محاكاة الفشل لمشاهدة التراجع التلقائي، ثم ألغِ المحاكاة للحفظ الفعلي.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي10 أسئلة تنتظرك +
أين يجب تنفيذ طلبات التعديل POST وPUT وDELETE داخل مكون React؟
تنطلق عمليات التعديل استجابةً لفعل صريح من المستخدم لذا مكانها الصحيح داخل دوال الأحداث مثل onSubmit وonClick.
أي حقل في رؤوس HTTP يخبر الخادم بأن جسم الطلب المُرسل عبر fetch() منسق بصيغة JSON؟
يعلن الرأس Content-Type بقيمة application/json أن البيانات المرسلة بصيغة JSON.
عند تمرير كائن JavaScript كجسم لطلب POST في fetch() يجب تحويله إلى نص عبر JSON.stringify(payload).
صواب؛ لأن تمرير الكائن الخام دون JSON.stringify يحوله إلى النص غير الصالح "[object Object]".
بعد نجاح طلب الحذف DELETE للمهمة t-2 على الخادم، كيف تزيلها بنقاء من مصفوفة الحالة في React؟
باستخدام التحديث الدالي مع filter لاستبعاد العنصر المحذوف دون تعديل المصفوفة الأصلية.
لماذا يوضع السطر setIsSubmitting(false) داخل كتلة finally بدلاً من نهاية كتلة try فقط؟
لأنه عند فشل الطلب والانتقال إلى catch، تضمن finally إعادة تفعيل الزر ليتمكن المستخدم من المحاولة مجدداً.
يتطلب التحديث المتفائل دائماً الاحتفاظ بنسخة من الحالة السابقة للتراجع الفوري إليها إذا رفض الخادم التعديل.
صواب؛ فبدون نسخة التراجع ستظهر للمستخدم بيانات غير محفوظة فعلياً على الخادم عند الفشل.
في أي عملية مما يلي يُعد التحديث المتفائل خياراً هندسياً غير مناسب وخطراً؟
عمليات الدفع المالي والحجوزات الحرجة تتطلب انتظار التأكيد الرسمي من الخادم قبل إظهار النجاح.
لماذا يُعد تخزين رموز جلسات المصادقة الحساسة داخل window.localStorage مخاطرة أمنية؟
لأن أي كود JavaScript يعمل في الصفحة، بما في ذلك ثغرات XSS، يستطيع قراءة localStorage وسرقة الرمز السري.
ملفات الارتباط المزودة بسمة HttpOnly لا يمكن قراءتها عبر document.cookie في JavaScript مما يحميها من سرقة XSS.
صواب؛ يتولى المتصفح إرسالها في طبقة الشبكة دون كشف قيمتها لأي سكريبت في الصفحة.
أي دالة مصفوفات نقية تستبدل كائناً محدّثاً واحداً داخل الحالة مع الإبقاء على بقية العناصر دون تغيير؟
تعيد الدالة map() مصفوفة جديدة يُستبدل فيها السجل المطابق فقط بنقاء.