الصنف React.Component

من موسوعة حسوب

تحتوي هذه الصفحة على مرجع مُفصَّل لواجهة برمجة التطبيقات (API) لتعريف صنف مكوّن React. سنفترض أنك على معرفة بمفاهيم React الأساسية، مثل الخاصيّات props والمكوّنات، بالإضافة إلى الحالة ودورة حياة المكوّنات. إن لم تكن كذلك فاطلع عليها أولًا.

لمحة عامة

تُتيح لك React أن تُعرِّف المكوّنات كأصناف أو دوال. تُزوّدنا المكوّنات المُعرَّفة كأصناف بميزات أكثر حاليًّا والتي سنشرحها بالتفصيل هنا. لتعريف صنف مكوّن React تحتاج إلى أن تمتد إلى الصنف React.Component:

class Welcome extends React.Component {
  render() {
    return <h1>أهلًا {this.props.name}</h1>;
  }
}

التابع الوحيد الذي يجب عليك تعريفه في الصنف الفرعي الناتج عن الصنف React.Component هو render()‎، أمّا بقية التوابع المذكورة في هذه الصفحة هي اختياريّة.

نوصي بشدّة ألّا تُنشِئ أصنافًا أساسيّة للمكوّنات خاصّة بك، ففي مكوّنات React تتحقّق إعادة استخدام الشيفرة بشكل أساسي عبر التركيب (composition) بدلًا من الوراثة (inheritance).

ملاحظة: لا تُجبِرك React على استخدام صياغة أصناف ES6. إن كنت تفضّل تجنّب ذلك فبإمكانك استخدام الوحدة create-react-class أو أي تجريد مُخصَّص مماثل بدلًا من ذلك. انظر إلى استخدام React بدون ES6 لتعلّم المزيد.

دورة حياة المكوّن

يمتلك كل مكوّن توابع دورة حياة متعدّدة والتي تستطيع تجاوزها لتنفيذ الشيفرة في أوقات مُحدَّدة. تستطيع استخدام مُخطّط دورة حياة المكوّنات هذا. في القائمة التالية سنكتب أسماء توابع دورة الحياة الشائعة بالخط العريض. أما البقية فهي موجودة لحالات الاستخدام النادرة نسبيًّا.

الوصل (mounting)

تُستدعى هذه التوابع بالترتيب التالي عند إنشاء نسخة من المكوّن وإدخالها إلى DOM:

  • constructor()‎.
  • static getDerivedStateFromProps()‎.
  • render()‎.
  • componentDidMount()‎.

ملاحظة: يُعتبر هذا التابع قديمًا ويجب أن تتجنّب استخدامه في الشيفرة الجديدة:

  • UNSAFE_componentWillMount()‎.

التحديث

يُمكِن أن يحصل التحديث عن طريق التغييرات في الخاصيّات أو الحالة. تُستدعى هذه التوابع بالترتيب التالي عند إعادة تصيير المكوّن:

  • static getDerivedStateFromProps()‎.
  • shouldComponentUpdate()‎.
  • render()‎.
  • getSnapshotBeforeUpdate()‎.
  • componentDidUpdate()‎.

ملاحظة: تُعتبر هذه التوابع قديمة ويجب أن تتجنّب استخدامها في الشيفرة الجديدة:

  • UNSAFE_componentWillUpdate()‎.
  • UNSAFE_componentWillReceiveProps()‎.

الفصل (unmounting)

يُستدعى هذا التابع عند إزالة المكون من DOM:

  • componentWillUnmount()‎.

معالجة الأخطاء

يُستدعى هذا التابع عند وجود خطأ أثناء التصيير، أو في تابع دورة حياة المكوّن، أو في الدالة البانية لأي من المكوّنات الأبناء.

componentDidCatch()‎.

واجهات برمجة التطبيق الأخرى

يُعطينا كل مكوّن بواجهات برمجة تطبيق أخرى:

  • setState()‎.
  • forceUpdate()‎.

خاصيّات الصنف

  • defaultProps.
  • displayName.

خاصيّات النسخة (Instance)

  • props.
  • state.

مرجع

أشيع توابع دورة الحياة المستخدمة

تُغطّي التوابع في هذا القسم معظم حالات الاستخدام التي ستصادفها أثناء إنشاء مكوّنات React. للحصول على مرجع لمخطط بصري انظر إلى مخطط دورة حياة المكوّنات.

render()‎

render()

التابع render()‎ هو التابع الوحيد المطلوب وجوده في مكوّنات الأصناف.

عند استدعائه يجب أن يفحص this.props و this.state ويُعيد إحدى الأنواع التالية:

  • عناصر React: تُنشَأ عادةً عن طريق JSX. على سبيل المثال ‎<div />‎ و ‎<MyComponent />‎ هي عناصر React والتي تأمر React بتصيير عقدة DOM ومكوّن مُعرَّف من قبل المستخدم على التوالي وبالترتيب.
  • الأجزاء والمصفوفات: تسمح لك بإعادة عناصر متعددة من التابع render. انظر إلى توثيق الأجزاء للمزيد من التفاصيل.
  • المنافذ (Portals): تسمح لك بتصيير العناصر الأبناء إلى تفرعات مختلفة من DOM. انظر إلى توثيق المنافذ للمزيد من التفاصيل.
  • الأعداد والسلاسل النصيّة: تُصيَّر كعقد نصيّة في DOM.
  • القيم المنطقية (Booleans) أو null: لا تُصيِّر شيئًا. (موجودة في معظم الأحيان لدعم النمط ‎return test && <Child />‎ حيث يكون test هو قيمة منطقيّة).

يجب أن يكون التابع render()‎ نقيًّا، أي لا يُعدِّل حالة المكوّن، ويعيد نفس النتيجة في كل مرة يُستدعى فيها، ولا يتفاعل بشكل مباشر مع المتصفح.

إن أردت التفاعل مع المتصفح فأنجز العمل المطلوب ضمن التابع componentDidMount()‎ أو أي تابع من توابع دورة الحياة. إنّ الحفاظ على التابع render()‎ نقيًّا يزيد سهولة التفكير بمكوّناتك.

ملاحظة: لن يُستدعى التابع render()‎ إن أعاد التابع shouldComponentUpdate()‎ القيمة false.

constructor()‎

constructor(props)

إن لم تضع قيمة بدئية للحالة ولم تربط التوابع، فلن تحتاج إلى إضافة دالة بانية إلى مكوناتك.

تُستدعى الدالة البانية لمكوّن React قبل الوصل. عند إضافة الدالة البانية لصنف فرعي عن الصنف React.Component فيجب أن تستدعي super(props)‎ قبل أي جملة أخرى وإلّا ستكون this.props غير معرفة في الدالة البانية والذي قد يؤدي إلى أخطاء.

تستخدم الدوال البانية في React فقط لغرضين عادةً:

  • تهيئة الحالة المحلية عن طريق تعيين كائن إلى this.state.
  • ربط توابع معالج الأحداث إلى النسخة (instance).

يجب ألّا تستدعي setState()‎ في الدالة البانية، وإن كان مكوّنك يحتاج استخدام الحالة المحليّة فعيّن الحالة المبدئية إلى this.state مباشرة في الدالة البانية:

constructor(props) {
  super(props);
  // لا تستدعي this.setState() هنا
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

الدالة البانية هي المكان الوحيد الذي يجب أن تّعين فيه this.state بشكل مباشر، ففي جميع التوابع الأخرى يجب استخدام this.setState()‎ بدلًا من ذلك.

تجنّب تقديم أي تأثيرات جانبية أو اشتراكات في الدالة البانية، ولتلك الحالات استخدم التابع componentDidMount()‎.

ملاحظة: تجنّب نسخ الخاصيّات إلى الحالة، فهذا خطأ شائع:

constructor(props) {
 super(props);
 // لا تفعل هذا
 this.state = { color: props.color };
}

المشكلة هي أنّ هذا غير ضروري (حيث تستطيع استخدام this.props.color بشكل مباشر) ويُعطي أخطاء (لن تنعكس التحديثات على الخاصيّة color في الحالة).

استخدم هذا النمط إن كنت تريد عن قصد تجاهل تحديثات الخاصيّات. في تلك الحالة من المنطقي إعادة تسمية الخاصيّة إلى initialColor أو defaultColor. بإمكانك بعدها إجبار المكوّن على إعادة تعيين حالته الداخلية عن طريق تغيير المفتاح عند الضرورة.

اقرأ هذا المنشور حول تجنب الحالات المشتقة لتتعلم ما يجب فعله إن أردت أن تعتمد الحالة على الخاصيّات.

componentDidMount()‎

componentDidMount()

يُستدعى componentDidMount()‎ مباشرة بعد وصل المكوّن (إدخاله ضمن الشجرة). يجب أن نضع هنا التهيئة التي تتطلّب عقدة DOM. إن احتجت إلى تحميل بيانات من نقطة بعيدة فهذا التابع مكان جيد لبدء طلبات الشبكة.

يُعد هذا التابع أيضًا مكانًا جيّدًا لإعداد أي اشتراكات. إن فعلت ذلك فلا تنسَ إزالة الاشتراك في التابع componentWillUnmount()‎.

بإمكانك استدعاء setState()‎ مباشرة في التابع componentDidMount()‎. سيُطلِق تصييرًا إضافيًّا ولكن سيحدث ذلك قبل أن يُحدِّث المتصفح الشاشة. يضمن ذلك عدم رؤية المستخدم للحالة مباشرة على الرغم من استدعاء التابع render()‎ مرتين. استخدم هذا النمط بحذر لأنّه يسبب غالبًا مشاكل بالأداء. يجب في معظم الحالات أن تُعيّن الحالة المبدئية في الدالة البانية بدلًا من ذلك. ولكن قد يكون ذلك ضروريًّا لحالات مثل تلميحات الأدوات (tooltips) عندما تحتاج إلى تقدير عقدة DOM قبل تصيير شيء يعتمد على حجمه أو موقعه.

componentDidUpdate()‎

componentDidUpdate(prevProps, prevState, snapshot)

يُستدعى التابع componentDidUpdate()‎ مباشرة بعد حصول التحديث. لا يُستدعى هذا التابع من أجل التصيير المبدئي. استخدم هذا التابع كفرصة للعمل على DOM عند تحديث المكوّن. يُعد هذا التابع مكانًا جيّدًا لإتمام طلبات الشبكة طالما تُقارِن الخاصيّات الحالية مع الخاصيّات السابقة (أي قد يكون طلب الشبكة غير ضروريّ إن لم تتغير الخاصيّات):

componentDidUpdate(prevProps) {
  // استخدام نموذجي (لا تنس مقارنة الخاصيات)
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

بإمكانك استدعاء setState()‎ مباشرة في التابع componentDidUpdate()‎ ولكن انتبه أنّه يجب تغليفه ضمن شرط مثل المثال السابق وإلّا ستسبب حدوث حلقة لا نهائيّة وإعادة تصيير إضافيّة والتي رغم عدم وضوحها للمستخدم إلاّ أنّها تؤثّر على أداء المكوّن. إن كنت تحاول أن تعكس الحالة إلى الخاصيّة الآتية من الأعلى فيجب أن تستخدم الخاصيّة بشكل مباشر. اقرأ المزيد في تدوينة لماذا يُسبب نسخ الخاصيّات إلى الحالة أخطاء.

إن كان يعتمد مكوّنك تابع دورة الحياة getSnapshotBeforeUpdate()‎ (وهو أمرٌ نادر)، فستُمرَّر القيمة التي يُعيدها كُمعامل ثالث إلى التابع componentDidUpdate()‎. فيما عدا ذلك يكون هذا المُعامِل غير مُعرَّفًا.

ملاحظة: لن يُستدعى التابع componentDidUpdate()‎ إن أعاد التابع shouldComponentUpdate()‎ القيمة false.

componentWillUnmount()‎

componentWillUnmount()

يُستدعى التابع componentWillUnmount()‎ مباشرةً قبل فصل المكوّن وتدميره. نفّذ أي مسح ضروري في هذا التابع، مثل تعطيل العدادات، وإلغاء طلبات الشبكة، ومسح أي اشتراكات أنشأها التابع componentDidMount()‎.

لا يجب أن تستدعي التابع setState()‎ في التابع componentWillUnmount()‎ لأنّ المكوّن لن يُعاد تصييره. حالما تُفصَل نسخة المكوّن فلن تُوصل مرة أخرى.

توابع دورة الحياة نادرة الاستخدام

تستخدم التوابع المذكورة في هذا القسم في حالات نادرة، وهي مفيدة من حين لآخر، ولكن لن تحتاجها معظم مكوّناتك. تستطيع أن ترى معظم هذه التوابع في مخطط توابع دورة حياة المكوّنات إن ضغطت على مربع التأشير "Show less common lifecycles" الموجود في الأعلى.

shouldComponentUpdate()‎

shouldComponentUpdate(nextProps, nextState)

استخدم التابع shouldComponentUpdate()‎ لتُعلِم React إن كان ناتج المكوّن لا يتأثر بالتغيير الحالي للخاصيّات أو الحالة. السلوك الافتراضي هو إعادة التصيير عند كل تغيير للحالة، وفي معظم الحالات ستعتمد على هذا السلوك.

يُستدعى التابع shouldComponentUpdate()‎ قبل التصيير عند استقبال الخاصيّات أو الحالة. القيمة الافتراضية هي true. لا يُستدعى هذا التابع للتصيير المبدئي أو عند استخدام التابع forceUpdate()‎.

يتواجد هذا التابع كتحسين للأداء فقط، لا تعتمد عليه لمنع التصيير، حيث يقود ذلك إلى أخطاء. انظر في استخدام الصنف PureComponent المُضمَّن بدلًا من كتابة التابع shouldComponentUpdate()‎ بشكلٍ يدوي. يُنفِّذ الصنف PureComponent مقارنة ضئيلة للخاصيّات والحالة ويُقلِّل فرصة تجاوز تحديث ضروري.

إن كنت متأكدًا من أنّك تريد كتابته بشكل يدوي فيجب أن تقارن this.props مع nextProps و this.state مع nextState وتُعيد القيمة false لتخبر React بإمكانية تجاوز التحديث. انتبه إلى أنّ إعادة القيمة false لا تمنع المكوّنات الأبناء من إعادة التصيير عند تغيير حالتها.

لا نوصي بإجراء اختبارات مفصلة للتساوي أو استخدام التابع JSON.stringify()‎ ضمن shouldComponentUpdate()‎، فهذا غير فعال وسيؤثر على الأداء بشكل كبير.

حاليًّا إن أعاد التابع shouldComponentUpdate()‎ القيمة false، فلن تُستدعى التوابع UNSAFE_componentWillUpdate()‎ أو render()‎ أو componentDidUpdate()‎. في المستقبل قد تُعامل React التابع shouldComponentUpdate()‎ كتلميح بدلًا من توجيه صارم، وقد تؤدي إعادة القيمة false إلى إعادة تصيير المكوّن.

static getDerivedStateFromProps()‎