حالة المكونات
ماذا يفعل التابع setState
؟
يُجدوِل التابع setState()
تحديثًا لكائن حالة المكوّن state
. عندما تتغير الحالة يستجب المكوّن بإعادة التصيير.
ما الفرق بين الحالة state
والخاصيّات props
؟
الخاصيّات props
(اختصارًا للكلمة properties) و الحالة state
كلاهما عبارة عن كائنات JavaScript مجرّدة. وفي حين أنّ كلاهما يحمل معلومات تؤثر في ناتج التصيير، فهما مختلفان بطريقة واحدة هامة، حيث تُمرَّر الخاصيّات إلى المكوّن (بشكل مماثل لمُعاملات الدالة) بينما تُدار الحالة state
ضمن المكوّن (بشكل مشابه للمتغيرات المعرفة بداخل الدالة).
هنا تجد مصادر جيدة لقراءة المزيد حول استخدام الخاصيّات والحالة:
لماذا يُعطيني التابع setState
القيمة الخاطئة؟
في React تُمثل this.props
و this.state
القيم المُصيَّرة، أي المعروضة على الشاشة حاليًّا.
تكون استدعاءات التابع setState
غير متزامنة، فلا تعتمد على this.state
لتعكس القيمة الجديدة للحالة بشكل مباشر بعد استدعاء التابع setState. مرر دالة تحديث بدلًا من تمرير كائن إن احتجت لحساب القيم بناءً على الحالة الحاليّة (انظر في الأسفل للمزيد من التفاصيل).
مثال عن شيفرة لن تعمل كما هو متوقع:
incrementCount() {
// ملاحظة: لن يعمل هذا كما هو متوقع
this.setState({count: this.state.count + 1});
}
handleSomething() {
// فلنقل أنّ this.state.count تبدأ بالقيمة 0
this.incrementCount();
this.incrementCount();
this.incrementCount();
// عندما تعيد React تصيير المكون فستصبح قيمة this.state.count 1
// ولكنك كنت تتوقعها أنها 3
// هذا لأنّ الدالة incrementCount()
// تقرأ this.state.count
// ولكن لا تحدث React قيمتها حتى إعادة تصيير المكون
// لذا ستقرأ React قيمة هذه الدالة على أنها صفر في كل مرة وستعينها للقيمة واحد
// الحل موصوف لاحقًا
}
انظر في الأسفل لمعرفة كيفية إصلاح هذه المشكلة.
كيف أحدث الحالة بقيم تعتمد على الحالة الحالية؟
مرر دالة بدلًا من كائن إلى التابع setState
لضمان استخدام الاستدعاء دائمًا لآخر إصدار من الحالة.
ما الفرق بين تمرير كائن أو دالة إلى التابع setState
؟
يسمح لك تمرير دالة التحديث بالوصول إلى قيمة الحالة الحالية بداخل دالة التحديث. وبما أنّ استدعاءات التابع setState
مجدولة سيسمح لك ذلك بسلسلة التحديثات وضمان أنّها تبني فوق بعضها بدلًا من التعارض فيما بينها:
incrementCount() {
this.setState((state) => {
// هام: اقرأ قيمة prevState بدلًا من this.state عند التحديث
return {count: state.count + 1}
});
}
handleSomething() {
// فلنفترض أنّ قيمة this.state.count تبدأ من الصفر
this.incrementCount();
this.incrementCount();
this.incrementCount();
// إن قرأت قيمة this.state.count الآن فستكون لا زالت صفر
// ولكن عندما يعاد تصيير المكون فستصبح 3
}
متى يكون التابع setState
غير متزامن؟
حاليًّا التابع setState غير متزامن بداخل معالج الأحداث.
إن كان المكوّن الأب Parent
والمكوّن الابن Child
يستدعيان التابع setState
خلال حدث النقر يضمن لنا عدم التزامن ألّا يُعاد تصيير المكوّن الابن Child
مرتين، وبدلًا من ذلك تمسح React تحديثات الحالة في نهاية أحداث المتصفّح. ينتج عن هذا تحسين هام في الأداء في التطبيقات الكبيرة.
لا يزال هذا تفصيلًا تنفيذيًّا لذا تجنّب الاعتماد عليه بشكل مباشر. في الإصدارات القادمة ستطبّق React التحديثات بشكل افتراضي في حالات أكثر.
لماذا لا تُحدِّث React قيمة this.state
بشكلٍ متزامن؟
كما تحدثنا في القسم السابق، تنتظر React عن قصد حتى تستدعي جميع المكوّنات التابع setState()
في مُعالِجات أحداثها قبل البدء بإعادة التصيير. يُسرِّع هذا الأداء عن طريق تجنّب إعادة التصيير غير الضروريّة.
على أيّة حال قد لا تزال تتساءل لماذا لا تُحدِّث React قيمة this.state
بشكل مباشر وبدون إعادة التصيير.
هنالك سببان رئيسيّان:
- يخرق هذا التوافقيّة بين الخاصيّات
props
والحالةstate
، مسببًا مشاكل من الصعب تنقيحها. - سيجعل هذا من بعض الميّزات التي نعمل عليها مستحيلة التطبيق.
يشرح هذا التعليق في GitHub بالتفصيل أمثلة عن هذا.
هل ينبغي أن أستخدم مكتبات إدارة الحالة مثل Redux أو MobX؟
من الأفضل في البداية التعرّف على React أولًا قبل إضافة مكتبات أخرى. بإمكانك بناء تطبيقات معقدة وكبيرة باستخدام React فقط.