لدورة حياة مكوّن React ثلاث مراحل: التركيب (يظهر المكوّن على الشاشة لأول مرة)، والتحديث (يُعرض من جديد لأن حالته أو props أو السياق تغيّر)، والإزالة (يُحذف). في مكوّنات الدوال تستجيب لهذه المراحل بـ useEffect: التأثير ذو [] يعمل بعد التركيب، والتأثير ذو الاعتماديات يعمل بعد التحديثات التي تغيّرت فيها، والدالة التي يُرجعها التأثير تعمل عند الإزالة.
تُظهر وحدة التحكم أسفل المعاينة كل مرحلة لحظة حدوثها.
تبدأ وحدة التحكم بـ render, count = 0 وmounted. كل نقرة على Count تسجّل عرضًا وتحديثًا. يسجّل Unmount السطر unmounted؛ ويبدأ Mount again من جديد من count = 0، لأن الحالة رُميت مع المكوّن.
التركيب
يُركَّب المكوّن في أول مرة تعرضه فيها React في موضع معين من الشجرة. تستدعي React الدالة، وتنشئ عقد DOM من JSX التي تُرجعها، وتدرجها، وتترك المتصفح يرسم، ثم تشغّل تأثيرات المكوّن.
useEffect(fn, []) هو المكان الذي تبدأ فيه الأشياء التي ينبغي أن تبقى ما دام المكوّن على الشاشة: اشتراك، أو مؤقت، أو اتصال، أو طلب بيانات أول. يعمل بعد الرسم، فلا يؤخر الإطار الأول أبدًا.
التحديث
يتحدث المكوّن (يُعرض من جديد) عندما:
- تتغير حالته الخاصة،
- يُعرض أبوه من جديد (افتراضيًا، يُعرض كل ابن أيضًا، سواء تغيّرت props الخاصة به أم لا)،
- يتغير سياق يقرؤه.
بعد كل تحديث، تشغّل React التأثيرات التي تغيّرت اعتمادياتها. وقبل تشغيل تأثير من جديد، تشغّل دالة التنظيف السابقة لذلك التأثير، فيُغلق الاشتراك القديم قبل أن يُفتح الجديد. تتناول صفحة العرض ما يطلق العرض بتفصيل أكبر.
الإزالة
يُزال المكوّن عندما يتوقف أبوه عن عرضه (كما يفعل {show && <Counter />} أعلاه)، أو عندما يتغير key الخاص به، أو عندما يُزال الجذر نفسه. تحذف React عقد DOM الخاصة به، وتشغّل دالة تنظيف كل تأثير، وترمي حالته. وإذا ظهر المكوّن مرة أخرى لاحقًا، فهو نسخة جديدة تمامًا.
الآباء والأبناء
تعرض React من الأعلى إلى الأسفل لكنها تشغّل التأثيرات من الأسفل إلى الأعلى. عندما يُركَّب أب وابن معًا، تعمل تأثيرات الابن أولًا، فيستطيع تأثير الأب الاعتماد على أن أبناءه مُعدّون بالكامل.
يسجّل التركيب render Parent وrender Child وChild effect وParent effect. اضغط Remove فتعمل دوال التنظيف بالعكس: Parent cleanup، ثم Child cleanup.
مرحلتا العرض والإثبات
يمر كل تحديث بمرحلتين:
- مرحلة العرض. تستدعي React مكوّناتك لتحسب JSX الجديدة وتقارنها بالنتيجة السابقة. لا يتغير شيء على الشاشة بعد. قد توقف React هذا العمل مؤقتًا، أو تشغّله مرتين في StrictMode، أو ترميه، ولهذا يجب أن يكون العرض نقيًا: لا طلبات، ولا اشتراكات، ولا تغييرات في متغيرات خارجية.
- مرحلة الإثبات. تطبّق React الفروق على DOM، وتربط المراجع، وتشغّل دوال
useLayoutEffect، كل ذلك قبل أن يرسم المتصفح. تعمل دوالuseEffectبعد ذلك، عادة بعد أن يرسم المتصفح. وعندما يكون التحديث قد نتج عن نقرة أو إدخال منفصل آخر، تشغّلها React مباشرة بعد تأثيرات التخطيط بدلًا من ذلك، فتكون النتيجة جاهزة قبل الحدث التالي.
كل نقرة تسجّل الأسطر الثلاثة بالترتيب نفسه. ليس لهذه التأثيرات مصفوفة اعتماديات، فتعمل بعد كل عرض. أضف [] كوسيط ثانٍ إلى كليهما فلا يعملان إلا بعد العرض الأول.
توابع دورة الحياة في الأصناف وما يقابلها من الخطافات
تكتب مكوّنات الأصناف دورة الحياة كتوابع مسماة. ستصادفها في الكود الأقدم وفي حدود الأخطاء، التي ما زالت تحتاج إلى صنف.
| تابع الصنف | متى يعمل | المقابل في مكوّن الدالة |
|---|---|---|
constructor | مرة واحدة، قبل العرض الأول | useState(initial) وuseRef |
static getDerivedStateFromProps | قبل كل عرض | احسب القيمة أثناء العرض، أو أعد الضبط بـ key |
render | كل عرض | جسم الدالة |
componentDidMount | بعد أن يكون العرض الأول في DOM | useEffect(fn, []) |
shouldComponentUpdate | قبل تحديث، ويمكنه تخطيه | React.memo (يُرجع الجواب المعاكس: «هل props متساوية») |
getSnapshotBeforeUpdate | قبل أن يتغير DOM مباشرة | لا خطاف مباشر؛ وأقرب شيء useLayoutEffect مع مرجع |
componentDidUpdate | بعد كل تحديث | useEffect(fn, [deps]) (يعمل أيضًا بعد التركيب) |
componentWillUnmount | قبل الإزالة | دالة التنظيف التي يُرجعها useEffect |
getDerivedStateFromError وcomponentDidCatch | عندما يرمي ابن خطأ | لا شيء: اكتب صنف حدود أخطاء |
إليك صنفًا يسجّل كل تابع أثناء تشغيله:
يسجّل التركيب constructor وgetDerivedStateFromProps وrender وcomponentDidMount. وتسجّل النقرة getDerivedStateFromProps وshouldComponentUpdate وrender وgetSnapshotBeforeUpdate وcomponentDidUpdate. اجعل shouldComponentUpdate يُرجع false فتتوقف النقرات عن تحديث الزر: تتغير الحالة، لكن React تتخطى العرض. تُظهر صفحة مكوّنات الأصناف كيف تحوّل صنفًا كهذا إلى دالة.
فكّر بالمزامنة لا بدورة الحياة
جدول الخطافات مفيد لقراءة الكود القديم، لكنه قد يقودك إلى كتابة تأثيرات تنسخ عادات الأصناف، مثل تأثير «تم التركيب» كبير واحد وتأثير «تم التحديث» كبير واحد. الأفضل أن تقرأ التأثير على أنه «أبقِ هذا الشيء الخارجي متزامنًا مع هذه القيم»: اتصل بـ roomId، وعندما يتغير roomId، اقطع الاتصال واتصل من جديد. اكتب تأثيرًا واحدًا لكل شيء تزامنه، واسرد القيم التي يستخدمها كاعتماديات، ودع React تقرر متى تشغّله.
الأسئلة الشائعة
ما مراحل دورة حياة مكوّن React؟
التركيب (تنشئ React المكوّن وتضع مخرجاته على الشاشة)، والتحديث (يُعرض من جديد لأن حالته أو props أو السياق تغيّر)، والإزالة (يُحذف وينظّف).
ما مقابل componentDidMount في الخطافات؟
useEffect(() => { ... }, []). مصفوفة الاعتماديات الفارغة تجعله يعمل مرة واحدة، بعد أن يصبح العرض الأول على الشاشة. والدالة التي يُرجعها هي مقابل componentWillUnmount.
ما مقابل componentDidUpdate في الخطافات؟
useEffect مع القيم التي تهمك في مصفوفة الاعتماديات، مثل [userId]. وخلافًا لـ componentDidUpdate، يعمل أيضًا بعد العرض الأول، فيغطي التركيب والتحديث معًا.
بأي ترتيب تعمل تأثيرات الأب والابن؟
تعرض React من الأب نزولًا، لكنها تشغّل التأثيرات من الأبناء صعودًا: يعمل تأثير الابن قبل تأثير أبيه. وعند الإزالة، تعمل دالة تنظيف الأب قبل دالة تنظيف الابن.
ما هما مرحلتا العرض والإثبات؟
في مرحلة العرض تستدعي React مكوّناتك لتحدد كيف يجب أن تبدو الشاشة؛ ويمكنها إيقاف هذا العمل مؤقتًا أو رميه. وفي مرحلة الإثبات تطبّق التغييرات على DOM وتشغّل تأثيرات التخطيط؛ ثم تعمل التأثيرات العادية بعد ذلك، عادة بعد أن يرسم المتصفح.