في هذا الدرس 6تصفّح المواضيع
تتميز النصوص والأرقام والقيم المنطقية في JavaScript بكونها قيماً أولية غير قابلة للتغيير، أما الكائنات (Objects) فهي مراجع قابلة للتعديل في مكانها، مما قد يغري المبرمج بكتابة cursor.x = 120 ثم setCursor(cursor). في React يجب معاملة أي كائن مخزن في الحالة بوصفه للقراءة فقط.
السبب الهندسي واضح: تفحص React تطابق الحالة السابقة والجديدة بالدالة Object.is()؛ فإذا عدلت نفس الكائن وأعدت تمرير مرجعه ذاته، تحسبها React غير متغيرة وتتجاهل إعادة رسم الشاشة تماماً.
import { useState } from 'react';
function BadgeMover() {
const [pos, setPos] = useState({ x: 0, y: 0 });
const moveX = () =>
setPos({ x: pos.x + 20, y: pos.y });
return <button onClick={moveX}>X: {pos.x}px</button>;
}
عندما يضم الكائن عدة خصائص مثل الاسم والمسمى الوظيفي والمدينة وتريد تعديل خاصية واحدة فقط، يوفر لك معامل انتشار الكائنات ({ ...profile, city: 'Alexandria' }) طريقة أنيقة ومختصرة لنسخ كافة الحقول السابقة داخل كائن جديد مع تحديث الحقل المطلوب فقط.
import { useState } from 'react';
function MentorCard() {
const [mentor, setMentor] = useState({
name: 'Nour',
level: 'Senior',
});
const up = () =>
setMentor({ ...mentor, level: 'Principal' });
return <button onClick={up}>{mentor.level}</button>;
}
إذا ارتبطت عدة حقول إدخال بخصائص كائن حالة واحد مثل العنوان والمسار والمدرب، يمكنك توحيد تحديثها بدالة معالجة واحدة رشيقة عبر مطابقة سمة name للحقل مع اسم الخاصية واستخدام المفاتيح المحسوبة (Computed Property Keys) بالصيغة [e.target.name]: e.target.value.
import { useState } from 'react';
function CourseDraft() {
const [f, setF] = useState({ title: 'React' });
const onEdit = (e) =>
setF({ ...f, [e.target.name]: e.target.value });
return (
<input name="title" value={f.title}
onChange={onEdit} />
);
}
انتبه دائماً إلى أن معامل الانتشار ... يجري نسخاً سطحياً (Shallow Copy) للمستوى الأول فقط! فإذا احتوى الكائن الخارجي على كائن فرعي بداخله، فإن نسخ المستوى الخارجي وحده يبقي الكائن الفرعي مشترك المرجع، وتعديله مباشرةً يلوث الحالة السابقة.
لتحديث خاصية داخل كائن متداخل بنقاء كامل في React القياسية، أنشئ نسخة جديدة لكل مستوى على مسار التغيير بدءاً من الكائن الأب وصولاً إلى الكائن الفرعي الهدف كما في المثال التفاعلي التالي:
import { useState } from 'react';
function PrefsBox() {
const [cfg, setCfg] = useState({
name: 'Studio',
ui: { compact: false },
});
const ui = { ...cfg.ui, compact: !cfg.ui.compact };
const flip = () => setCfg({ ...cfg, ui });
return <button onClick={flip}>Toggle UI</button>;
}
قبل الانتقال إلى مصفوفات الحالة في الدرس القادم، راجع المقارنة الشاملة بين أنماط التعديل المباشر المحظورة والبدائل النقية المعتمدة لتحديث الكائنات في React:
| العملية المطلوبة | التعديل المباشر المحظور | التحديث النقي المعتمد في React |
|---|---|---|
| تعديل خاصية سطحية | user.city = "Giza"; setUser(user) | setUser({ ...user, city: "Giza" }) |
| تحديث حقل بمفتاح ديناميكي | form[name] = val; setForm(form) | setForm({ ...form, [name]: val }) |
| تعديل خاصية داخل كائن فرعي | u.prefs.dark = true; setU({ ...u }) | setU({ ...u, prefs: { ...u.prefs, dark: true } }) |
| حذف خاصية من كائن الحالة | delete cfg.temp; setCfg(cfg) | const { temp, ...rest } = cfg; setCfg(rest) |
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
تعلمت في هذا الدرس سبب اعتماد React على مقارنة مراجع الكائنات بـ Object.is، واستبدلت التعديل المباشر بالنسخ السطحي عبر معامل الانتشار والمفاتيح المحسوبة وتحديث الكائنات المتداخلة بنقاء تام. الخطوة التالية: انتقل الآن إلى الدرس رقم 21 «تحديث المصفوفات في الحالة بطرق غير مغيرة» لمواصلة البناء التراكمي المتدرج في مسار React الحديث.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 Immutability عدم القابلية للتغيير المباشر
- معاملة كائنات الحالة الحالية للقراءة فقط وإنشاء نسخ ذات مراجع جديدة عند كل تحديث.
- للمراجعة
- 02 Shallow Copy النسخ السطحي
- نسخ خصائص المستوى الأول فقط من الكائن عبر معامل الانتشار مع بقاء الكائنات الداخلية بنفس المرجع.
- للمراجعة
- 03 Computed Property Name اسم الخاصية المحسوب
- صياغة الأقواس المربعة داخل الكائن لتحديد اسم المفتاح المراد تحديثه ديناميكياً وقت التشغيل.
- للمراجعة
- 04 Reference Equality تساوي المرجع بالذاكرة
- الفحص السريع لعنوان الذاكرة عبر Object.is الذي تحدد به React ما إذا كانت الحالة قد تغيرت.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 ES6 ES6
- ECMAScript 2015
- إصدار اللغة القياسي الذي قدم ميزة أسماء الخصائص المحسوبة داخل الكائنات.
- للمراجعة
- 02 ES2018 ES2018
- ECMAScript 2018
- إصدار اللغة الذي اعتمد رسمياً معامل الانتشار والجمع داخل الكائنات النصية.
- للمراجعة
- 03 UI UI
- User Interface
- واجهة المستخدم التي تتجمد عن التحديث إذا عُدل الكائن في مكانه دون إنشاء مرجع جديد.
- للمراجعة
- 04 RAM RAM
- Random-Access Memory
- الذاكرة التي تتشارك فيها الفروع غير المتغيرة مراجعها بكفاءة بين لقطات الحالة المتعاقبة.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
افتح محرر المسودة في محرر التجريب وأضف خاصية level للحالة مع حقل إدخال يستخدم نفس دالة handleChange المشتركة.
-
في مثال إعدادات مساحة العمل، أضف زر استعادة يعيد ضبط الكائن الفرعي preferences بنسخة جديدة للقيم الافتراضية.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي10 أسئلة تنتظرك +
لماذا يفشل الكود user.name = "Salma"; setUser(user); في تحفيز إعادة تصيير المكون في React؟
لأن الدالة Object.is ترى نفس مرجع الكائن في الذاكرة فتعتبر أن الحالة لم تتغير وتتخطى الرسم.
أي صياغة برمجية تحدث خاصية role فقط داخل كائن الحالة profile بنقاء مع الحفاظ على بقية خصائصه؟
تنشئ الصيغة { ...profile, role: "Architect" } كائناً جديداً ينسخ القديم ثم يحدث الحقل المطلوب.
ماذا يحدث إذا وضعت معامل الانتشار بعد الحقل الجديد بالخطأ مثل setDraft({ title: "New", ...draft })؟
تفوز الخاصية المتأخرة في كائنات JavaScript، فتطغى قيمة draft.title القديمة على القيمة الجديدة.
يقوم معامل الانتشار { ...account } باستنساخ عميق تلقائي لجميع الكائنات الفرعية المتداخلة بداخله.
خطأ؛ فمعامل الانتشار ينسخ المستوى الأول فقط نسخاً سطحياً وتبقى الكائنات الفرعية بنفس المرجع.
ما ميزة ES6 التي تتيح لدالة handleChange واحدة تحديث خاصية الكائن المطابقة لاسم الحقل e.target.name؟
تسمح أسماء الخصائص المحسوبة [e.target.name]: e.target.value بتحديد المفتاح ديناميكياً.
ما الطريقة النقية الصحيحة لتحديث الخاصية المتداخلة user.address.zip إلى "11511" في حالة React؟
يجب نسخ الكائن الخارجي user والكائن الفرعي user.address معاً بمعامل الانتشار.
تعديل كائن محلي أُنشئ للتو داخل نفس الدالة قبل تمريره إلى setPoint(next) يُعد آمناً لأن أي تصيير سابق لا يشير إليه.
صواب؛ فالتعديل المحلي لكائن وليد لم يدخل الحالة بعد لا يمس لقطات التصيير السابقة.
ما النمط النقي لحذف خاصية tempToken من كائن الحالة config دون استخدام الأمر المغير delete؟
تفكيك الباقي const { tempToken, ...rest } = config ثم تمرير rest ينشئ كائناً جديداً خالياً من المفتاح.
يساعد إبقاء هيكل كائنات الحالة مسطحاً وبسيطاً على تقليل تعقيد النسخ المتداخل ومنع أخطاء المراجع المشتركة.
صواب؛ فالهياكل المسطحة أسهل في التحديث وأقل عرضة لأخطاء التعديل السطحي.
ما دالة المقارنة القياسية في JavaScript التي تعتمدها React داخلياً للتحقق من تغير قيمة الحالة؟
تقارن React المرجع أو القيمة السابقة بالجديدة عبر الدالة القياسية Object.is().