الفرق بين المراجعتين ل"React/react component"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(تحديث الصفحة، والعنوان، والتصنيفات)
ط
سطر 45: سطر 45:
 
=== معالجة الأخطاء ===
 
=== معالجة الأخطاء ===
 
يُستدعى هذا التابع عند وجود خطأ أثناء التصيير، أو في تابع دورة حياة المكوّن، أو في الدالة البانية لأي من المكوّنات الأبناء.
 
يُستدعى هذا التابع عند وجود خطأ أثناء التصيير، أو في تابع دورة حياة المكوّن، أو في الدالة البانية لأي من المكوّنات الأبناء.
* <code>()static getDerivedStateFromError</code>
+
* <code>[[React/react component#static getDerivedStateFromError.28.29.E2.80.8E|()static getDerivedStateFromError]]</code>
 
* [[React/react component#componentDidCatch.28.29.E2.80.8E|<code>componentDidCatch()‎</code>]]
 
* [[React/react component#componentDidCatch.28.29.E2.80.8E|<code>componentDidCatch()‎</code>]]
  
سطر 96: سطر 96:
 
constructor(props) {
 
constructor(props) {
 
   super(props);
 
   super(props);
   // لا تستدعي this.setState() هنا
+
   // هنا this.setState() لا تستدعي
 
   this.state = { counter: 0 };
 
   this.state = { counter: 0 };
 
   this.handleClick = this.handleClick.bind(this);
 
   this.handleClick = this.handleClick.bind(this);
سطر 234: سطر 234:
  
 
==== حدود الأخطاء ====
 
==== حدود الأخطاء ====
[[React/error boundaries|حدود الأخطاء]] (Error boundaries) هي مكوّنات React التي تُمسِك أخطاء JavaScript في أي مكان من شجرة المكوّنات الفرعية عنها، وتُسجل الأخطاء، وتعرض واجهة مستخدم بديلة عن شجرة المكوّنات التي انهارت. تُمسِك حدود الأخطاء بالأخطاء خلال التصيير، وفي توابع دورة حياة المكوّنات، وفي الدوال البانية لكل شجرة المكوّنات الأدنى منها.
+
[[React/error boundaries|حدود الأخطاء]] (Error boundaries) هي مكوّنات React التي تُمسِك أخطاء [[JavaScript]] في أي مكان من شجرة المكوّنات المتفرعة عنها، وتُسجل الأخطاء، وتعرض واجهة مستخدم بديلة عن شجرة المكوّنات التي انهارت. تُمسِك حدود الأخطاء بالأخطاء خلال التصيير، وفي توابع دورة حياة المكوّنات، وفي الدوال البانية لكل شجرة المكوّنات الأدنى منها.
  
يُصبح مكوّن الصنف حدًّا للأخطاء إن كان يُعرِّف تابع دورة الحياة <code>componentDidCatch()</code>‎. يسمح لك استدعاء التابع <code>setState()‎</code> في التقاط أخطاء JavaScript غير المُتعامَل معها في المستوى الأدنى من الشجرة مع عرض واجهة مستخدم بديلة. استخدم حدود الأخطاء فقط من أجل التعافي من الأخطاء غير المتوقعة، لا تحاول استخدامها للتحكم بتدفق البيانات.
+
يُصبح مكوّن الصنف حدًّا للأخطاء إن كان يُعرِّف تابع دورة الحياة <code>componentDidCatch()</code>‎. يسمح لك استدعاء التابع <code>setState()‎</code> في التقاط أخطاء JavaScript التي لم تعالج في المستوى الأدنى من الشجرة مع عرض واجهة مستخدم بديلة. استخدم حدود الأخطاء فقط من أجل التعافي من الأخطاء غير المتوقعة، ولا تحاول استخدامها للتحكم بتدفق البيانات.
  
للمزيد من المعلومات انظر إلى [https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html التعامل مع الأخطاء في React 16].
+
للمزيد من المعلومات، انظر إلى [https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html التعامل مع الأخطاء في React 16].
  
 
'''ملاحظة:''' تُمسِك حدود الأخطاء فقط الأخطاء في المستويات الأدنى منها في شجرة المكوّنات، فلا يستطيع حد الخطأ أن يُمسِك بالأخطاء الحاصلة ضمنه.
 
'''ملاحظة:''' تُمسِك حدود الأخطاء فقط الأخطاء في المستويات الأدنى منها في شجرة المكوّنات، فلا يستطيع حد الخطأ أن يُمسِك بالأخطاء الحاصلة ضمنه.
سطر 308: سطر 308:
 
   }
 
   }
 
}
 
}
</syntaxhighlight>'''ملاحظة''': عند حدوث أي خطأ، يمكنك أن تصيير واجهة مستخدم احتياطية (fallback UI) مع التابع <code>componentDidCatch()‎</code> عبر استدعاء <code>setState</code>، ولكن هذا السلوك سيجري اهماله في إصدار مستقبلي. استعمال <code>static getDerivedStateFromError()‎</code> لمعالجة التصيير الاحتياطي عوضًا عن ذلك.
+
</syntaxhighlight>'''ملاحظة''': عند حدوث أي خطأ، يمكنك أن تصيير واجهة مستخدم احتياطية (fallback UI) مع التابع <code>componentDidCatch()‎</code> عبر استدعاء <code>setState</code>، ولكن هذا السلوك سيجري إهماله في إصدار مستقبلي. استعمل <code>static getDerivedStateFromError()‎</code> لمعالجة التصيير الاحتياطي عوضًا عن ذلك.
  
 
=== توابع دورة الحياة القديمة ===
 
=== توابع دورة الحياة القديمة ===

مراجعة 12:52، 24 فبراير 2019

تحتوي هذه الصفحة على مرجع مُفصَّل لواجهة برمجة التطبيقات (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:

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

التحديث

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

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

الفصل (unmounting)

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

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

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

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

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

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

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

مرجع

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

تُغطّي التوابع في هذا القسم معظم حالات الاستخدام التي ستصادفها أثناء إنشاء مكوّنات 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 فقط لغرضين عادةً:

يجب ألّا تستدعي 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()‎

static getDerivedStateFromProps(props, state)

يُستدعى التابع getDerivedStateFromProps مباشرةً قبل استدعاء تابع التصيير خلال الوصل المبدئي والتحديثات اللاحقة. يجب أن يُعيد كائنًا لتحديث الحالة، أو null لعدم تحديث شيء.

يوجد هذا التابع من أجل استخدامات نادرة عندما تعتمد الحالة على التغييرات في الخاصيّات مع مرور الوقت. على سبيل المثال قد يكون من المفيد تنفيذ المكوّن ‎<Transition>‎ والذي يقارن بين الأبناء السابقين واللاحقين ليقرر ما ينبغي تحريكه منها للداخل وللخارج.

يؤدي اشتقاق الحالة إلى تعقيد الشيفرة وصعوبة التفكير بمكوّناتك.

احرص على أن تكون على اطلاع على البدائل البسيطة له:

  • إن أردت إنجاز تأثير جانبي (side effect) (مثل الحصول على البيانات أو التحريك) استجابةً للتغيّر في الخاصيّات، فاستخدم تابع دورة الحياة componentDidUpdate.
  • إن أردت إعادة حساب بعض البيانات فقط عند تغيير الخاصيّات، فاستخدم مساعد التذكير بدلًا من ذلك.
  • إن أردت إعادة تعيين حالة ما عند تغيّر الخاصيّة، فانظر في إمكانية جعل المكوّن مضبوطًا بشكل كامل أو غير مضبوط مع استخدام المفاتيح.

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

انتبه إلى إطلاق هذا التابع عند كل تصيير بغض النظر عن السبب، وهذا على عكس UNSAFE_componentWillReceiveProps، والذي يُطلَق فقط عندما يُسبِّب المكوّن الأب إعادة التصيير وليس كنتيجة عن التابع setState المحلّي.

getSnapshotBeforeUpdate()‎

getSnapshotBeforeUpdate(prevProps, prevState)

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

حالة الاستخدام هذه ليست شائعة، ولكن قد تحدث في واجهات مستخدم مثل محادثة في تطبيق للدردشة يحتاج للتعامل مع موضع النزول في الصفحة بطريقة معينة.

يجب إعادة قيمة snapshot (أو null).

على سبيل المثال:

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
	// هل نضيف عناصر جديدة للقائمة؟
	// التقط موضع النزول في الصفحة بحيث نتمكن من ضبطه لاحقًا
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
	// إن كانت لدينا قيمة snapshot فقد أضفنا للتو عناصر جديدة
	// ضبط النزول في الصفحة بحيث لا تدفع العناصر الجديدة العناصر القديمة خارج نطاق الرؤية
	// قيمة snapshot هي القيمة المعادة من التابع getSnapshotBeforeUpdate
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

من المهم في المثال السابق قراءة الخاصيّة scrollHeight في getSnapshotBeforeUpdate لأنّه قد توجد تأخيرات بين توابع طور التصيير (مثل التابع render) وتوابع طور التطبيق (مثل getSnapshotBeforeUpdate و componentDidUpdate).

حدود الأخطاء

حدود الأخطاء (Error boundaries) هي مكوّنات React التي تُمسِك أخطاء JavaScript في أي مكان من شجرة المكوّنات المتفرعة عنها، وتُسجل الأخطاء، وتعرض واجهة مستخدم بديلة عن شجرة المكوّنات التي انهارت. تُمسِك حدود الأخطاء بالأخطاء خلال التصيير، وفي توابع دورة حياة المكوّنات، وفي الدوال البانية لكل شجرة المكوّنات الأدنى منها.

يُصبح مكوّن الصنف حدًّا للأخطاء إن كان يُعرِّف تابع دورة الحياة componentDidCatch()‎. يسمح لك استدعاء التابع setState()‎ في التقاط أخطاء JavaScript التي لم تعالج في المستوى الأدنى من الشجرة مع عرض واجهة مستخدم بديلة. استخدم حدود الأخطاء فقط من أجل التعافي من الأخطاء غير المتوقعة، ولا تحاول استخدامها للتحكم بتدفق البيانات.

للمزيد من المعلومات، انظر إلى التعامل مع الأخطاء في React 16.

ملاحظة: تُمسِك حدود الأخطاء فقط الأخطاء في المستويات الأدنى منها في شجرة المكوّنات، فلا يستطيع حد الخطأ أن يُمسِك بالأخطاء الحاصلة ضمنه.

static getDerivedStateFromError()‎

static getDerivedStateFromError(error)

يُستدعَى تابع دورة الحياة هذا بعد أن يُرمَى الخطأ عبر مكون سليل (descendant component). يستقبل هذا التابع الخطأ الذي رُمِيَ كوسيط ويجب أن يعيد قيمةً لتحديث الحالة.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // تحديث الحالة وبذلك ستظهر عملية التصيير التالية واجهة
    // (fallback UI) المستخدم الاحتياطية
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      // يمكنك تصيير أية واجهة مستخدم احتياطية مخصصة
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

ملاحظة: يُستدعَى getDerivedStateFromError()‎ أثناء طور التصيير، لذا لا يُسمح بتنفيذ أي تأثيرات جانبية. من أجل حالات الاستعمال تلك، استعمل componentDidCatch()‎ عوضًا عنه.

componentDidCatch()‎

componentDidCatch(error, info)

يُستدعَى تابع دورة الحياة هذا بعد أن يُرمَى خطأ عبر مكون سليل (descendant component). يستلم هذا التابع معاملين:

  1. error: الخطأ الذي رُمِي.
  2. info: كائن مع المفتاح componentStack الذي يحوي معلومات عن المكون الذي رمى الخطأ.

يُستدعَى componentDidCatch()‎ أثناء طور التطبيق، لذا يُسمَح بالتأثيرات الجانبية. يجب أن يُستعمَل هذا التابع من أجل أشياء مثل تسجيل الأخطاء:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // تحديث الحالة، لذا ستظهر عملية التصيير التالية واجهة
    // المستخدم الاحتياطية
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // Example "componentStack":
    //   in ComponentThatThrows (created by App)
    //   in ErrorBoundary (created by App)
    //   in div (created by App)
    //   in App
    logComponentStackToMyService(info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // يمكنك تصيير أية واجهة مستخدم احتياطية مخصصة
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

ملاحظة: عند حدوث أي خطأ، يمكنك أن تصيير واجهة مستخدم احتياطية (fallback UI) مع التابع componentDidCatch()‎ عبر استدعاء setState، ولكن هذا السلوك سيجري إهماله في إصدار مستقبلي. استعمل static getDerivedStateFromError()‎ لمعالجة التصيير الاحتياطي عوضًا عن ذلك.

توابع دورة الحياة القديمة

تعتبر توابع دورة الحياة التالية قديمة. لا تزال تعمل ولكن لا نوصي باستخدامها في الشيفرة الجديدة. بإمكانك تعلم المزيد حول الانتقال من التوابع القديمة في هذا المنشور.

UNSAFE_componentWillMount()‎

UNSAFE_componentWillMount()

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

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

هذا هو التابع الوحيد من توابع دورة الحياة الذي يُستدعى لأجل التصيير على الخادم.

ملاحظة: كان يُسمّى هذا التابع سابقًا componentWillMount وسيبقى هذا الاسم يعمل حتى الإصدار 17. استخدم rename-unsafe-lifecycles codemod لتحديث مكوّناتك تلقائيًّا.

UNSAFE_componentWillReceiveProps()‎

UNSAFE_componentWillReceiveProps(nextProps)

ملاحظة: كان يُسمّى هذا التابع سابقًا componentWillReceiveProps وسيبقى هذا الاسم يعمل حتى الإصدار 17. استخدم rename-unsafe-lifecycles codemod لتحديث مكوّناتك تلقائيًّا.

ملاحظة: يقود استخدام تابع دورة الحياة هذا إلى أخطاء وعدم توافقية، لذا سيُهمَل في المستقبل.

إن احتجت إلى إنجاز تأثير جانبي (مثل الحصول على البيانات أو التحريك) استجابةً إلى تغيّر في الخاصيّات، فاستخدم التابع componentDidUpdate بدلًا من ذلك.

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

إن استخدمت التابع componentWillReceiveProps لإعادة حساب بعض البيانات عند تغيّر الخاصيّات فقط، فاستخدم مُساعِد التذكير بدلًا من ذلك.

إن استخدمت التابع componentWillReceiveProps لإعادة تعيين الحالة عند تغيّر الخاصيّات، فانظر في جعل المكوّن مضبوطًا بشكل كامل أو غير مضبوط بشكل كامل مع استخدام المفاتيح بدلًا من ذلك.

في بعض الحالات النادرة قد ترغب باستخدام تابع دورة الحياة getDerivedStateFromProps كملاذ أخير.

يُستدعَى التابع UNSAFE_componentWillReceiveProps()‎ قبل أن يستقبل المكوّن الموصول خاصيّات جديدة. إن أردت تحديث الحالة استجابةً لتغيّر الخاصيّة (على سبيل المثال إعادة تعيينها) فبإمكانك مقارنة this.props و nextProps وإتمام تغيير الحالة باستخدام this.setState()‎.

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

لا تستدعي React التابع UNSAFE_componentWillReceiveProps()‎ مع الخاصيّات المبدئية خلال الوصل، وتستدعيه فقط إذا تحدثت بعض خاصيّات المكوّن. لا يُطلِق استدعاء this.setState()‎ بشكلٍ عام التابع UNSAFE_componentWillReceiveProps()‎.

UNSAFE_componentWillUpdate()‎

UNSAFE_componentWillUpdate(nextProps, nextState)

ملاحظة: كان يُسمّى هذا التابع سابقًا componentWillUpdate وسيبقى هذا الاسم يعمل حتى الإصدار 17. استخدم rename-unsafe-lifecycles codemod لتحديث مكوّناتك تلقائيًّا.

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

انتبه إلى أنّك لا تستطيع استدعاء this.setState()‎ هنا، ولا ينبغي أن تفعل أي شيء آخر (مثلًا تطبيق إجراء في Redux) يقود إلى إطلاق تحديث مكوّن React قبل إعادة قيمة التابع UNSAFE_componentWillUpdate()‎.

يُمكِن استبدال هذا التابع بالتابع componentDidUpdate()‎. إن كُنتَ تقرأ من DOM في هذا التابع (على سبيل المثال حفظ موضع النزول في الصفحة) فبإمكانك نقل هذا المنطق إلى التابع getSnapshotBeforeUpdate()‎.

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

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

على عكس توابع دورة الحياة السّابقة (والتي تستدعيها React لأجلك)، فإنّ التوابع التالية هي توابع تستطيع استدعاءها من مكوّناتك.

هنالك فقط تابعان: setState()‎ و forceUpdate()‎.

setState()‎

setState(updater[, callback])

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

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

لا يُحدِّث التابع setState()‎ المكوّن فورًا، فقد يُؤجل التحديث حتى وقتٍ لاحق. يجعل هذا من قراءة this.state مباشرةً بعد استدعاء setState()‎ أمرًا خاطئًا. استخدم بدلًا من ذلك التابع componentDidUpdate أو رد النداء setState (على الشكل setState(updater, callback)‎)، والتي من المضمون إطلاقها بعد تطبيق التحديث. إن احتجت إلى تعيين الحالة بناءً على الحالة السابقة، فاقرأ حول الوسيط updater الذي سنذكره بعد قليل.

يقود setState()‎ إلى إعادة التصيير دومًا ما لم يُرجِع التابع shouldComponentUpdate()‎ القيمة false. إن كنت تستخدم الكائنات القابلة للتعديل ولم يكن بالإمكان تطبيق منطق التصيير الشرطي ضمن التابع shouldComponentUpdate()‎، فستتجنّب إعادة التصيير غير الضرورية باستدعاء setState()‎ فقط عند اختلاف الحالة الجديدة.

الوسيط الأول هو الدالة updater والتي يكون شكلها كما يلي:

(prevState, props) => stateChange

prevState هو مرجع إلى الحالة السابقة، فلا يجب تعديله بشكل مباشر، بل يجب تمثيل التغييرات عن طريق بناء كائن جديد اعتمادًا على المُدخلات من prevState و props. فلنفترض مثلًا أنّنا أردنا زيادة القيمة الموجودة في الحالة بإضافة props.step إليها:

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

من المضمون أن تكون الحالة السابقة prevState والخاصيّات props التي تستقبلها الدالة updater بآخر تحديث. يُدمَج ناتج الدالة updater بشكل ضئيل مع الحالة السابقة prevState.

الوسيط الثاني للتابع setState()‎ هو دالة رد نداء اختياريّة تُنفَّذ حالما يكتمل التابع setState()‎ ويُعاد تصيير المكوّن. نوصي بشكلٍ عام باستخدام التابع componentDidUpdate()‎ لأجل هذا المنطق.

بإمكانك بشكل اختياري تمرير كائن كوسيط أول للتابع setState()‎ بدلًا من تمرير دالة:

setState(stateChange[, callback])

يؤدي هذا إلى دمج ضئيل لتغيير الحالة stateChange مع الحالة الجديدة، على سبيل المثال لضبط كمية العنصر موجود في سلّة الشراء:

this.setState({quantity: 2})

هذا الشكل للتابع setState()‎ غير متزامن أيضًا، ويُمكِن تجميع الاستدعاءات المتعددة خلال نفس الدور مع بعضها. مثلًا إن حاولت زيادة كمية العنصر أكثر من مرّة في نفس الدورة فسينتج عن ذلك ما يلي:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

تتجاوز الاستدعاءات المتلاحقة القيم من الاستدعاءات السابقة في نفس الدورة، لذا ستزداد الكمية فقط مرّة واحدة. إن كانت الحالة التالية تعتمد على الحالة السابقة فنوصي باستخدام شكل الدالة updater بدلًا من ذلك:

this.setState((prevState) => {
  return {quantity: prevState.quantity + 1};
});

للمزيد من التفاصيل انظر:

forceUpdate()‎

component.forceUpdate(callback)

عندما تتغيّر حالة أو خاصيّات مكوّناتك فسيعيد المكوّن تصيير نفسه بشكلٍ افتراضي. إن كان تابع التصيير render()‎ لديك يعتمد على بعض البيانات الأخرى، فبإمكانك إخبار React بضرورة إعادة تصيير المكوّن عن طريق استدعاء التابع forceUpdate()‎.

سيُؤدي استدعاء التابع forceUpdate()‎ إلى استدعاء التابع render()‎ في المكوّن مع تجاوز التابع shouldComponentUpdate()‎. سيُطلِق ذلك توابع دورة الحياة الاعتيادية للمكوّنات الأبناء بما في ذلك التابع shouldComponentUpdate()‎ لكل ابن. وستبقى React تُحدِّث DOM فقط عند حصول تغيير.

يجب عليك أن تحاول تجنّب استخدام التابع forceUpdate()‎ وأن تقرأ القيم فقط من this.props و this.state في التابع render()‎.

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

defaultProps

يُمكِن تعريف defaultProps كخاصيّة لمكوّن الصنف نفسه لتعيين الخاصيّات الافتراضيّة للصنف. تُستخدَم هذه الخاصيّة لأجل الخاصيّات غير المُعرَّفة، ولكن ليس لأجل الخاصيّات null، على سبيل المثال:

class CustomButton extends React.Component {
  // ...
}

CustomButton.defaultProps = {
  color: 'blue'
};

إن تُعطى قيمة props.color فستُعيَّن بشكل افتراضي إلى ‎'blue'‎:

render() {
    return <CustomButton /> ; // props.color ستُعين إلى اللون الأزرق
  }

إن كانت props.color مُعيَّنة إلى null فستبقى null:

render() {
    return <CustomButton color={null} /> ; // props.color ستبقى null
  }

displayName

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

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

props

تحتوي this.props على الخاصيّات المُعرَّفة في السطر الذي استدعى هذا المكوّن. انظر إلى توثيق المكوّنات والخاصيّات لمقدمة حول الخاصيّات.

this.props.children هي خاصيّة مميزة مُعرَّفة عن طريق الوسم child في تعبير JSX بدلًا من وضعها ضمن الوسم نفسه.

state

تحتوي الحالة على بيانات خاصّة بهذا المكوّن والتي قد تتغيّر مع مرور الوقت. تُعرَّف الحالة من قبل المستخدم ويجب أن تكون كائن JavaScript مجرّد.

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

انظر إلى الحالة وتوابع دورة الحياة للمزيد من المعلومات حول الحالة.

لا تُعدِّل قيمة this.state بشكلٍ مباشر إطلاقًا، فقد يؤدي استدعاء setState()‎ بعدها إلى تبديل التغيير الذي أجريته. تعامل مع this.state كما لو أنّها قيمة غير قابلة للتعديل.

انظر أيضًا

مصادر