الحفاظ على الحالة وإعادة تعيينها
الحالة معزولة بين المكونات. يتتبع React الحالة التي تنتمي إلى أي مكون بناءً على مكانه في شجرة واجهة المستخدم. يمكنك التحكم في وقت الحفاظ على الحالة ووقت إعادة تعيينها بين عمليات إعادة التصيير.
سوف تتعلم
- متى يختار React الحفاظ على الحالة أو إعادة تعيينها
- كيفية إجبار React على إعادة تعيين حالة المكون
- كيف تؤثر المفاتيح والأنواع على ما إذا كانت الحالة محفوظة أم لا
الحالة مرتبطة بموقع في شجرة التصيير
يبني Reactأشجار التصييرلهيكل المكونات في واجهة المستخدم الخاصة بك.
عندما تعطي مكونًا حالة، قد تعتقد أن الحالة "تعيش" داخل المكون. لكن الحالة في الواقع محفوظة داخل React. يربط React كل جزء من الحالة التي يحتفظ بها بالمكون الصحيح بناءً على مكان وجود ذلك المكون في شجرة التصيير.
هنا، يوجد علامة JSX واحدة فقط<Counter />، لكنها مُصَيرة في موقعين مختلفين:
هذا هو شكلها كشجرة:


شجرة React
هذان عدادان منفصلان لأن كل منهما مُصَير في موقعه الخاص في الشجرة.لا تحتاج عادةً إلى التفكير في هذه المواقع لاستخدام React، لكن فهم كيفية عملها يمكن أن يكون مفيدًا.
في React، كل مكون على الشاشة له حالة معزولة تمامًا. على سبيل المثال، إذا صَيرت مكونينCounterجنبًا إلى جنب، سيكون لكل منهما حالته الخاصة والمستقلة،score و hover.
جرب النقر على كلا العدادين ولاحظ أنهما لا يؤثران على بعضهما البعض:
كما ترى، عند تحديث عداد واحد، يتم تحديث حالة ذلك المكون فقط:


تحديث الحالة
سيحتفظ React بالحالة طالما قمت بتصيير نفس المكون في نفس الموقع في الشجرة. لرؤية ذلك، زِد كلا العدادين، ثم أزل المكون الثاني بإلغاء تحديد خانة الاختيار "تصيير العداد الثاني"، ثم أضفه مرة أخرى بتحديده مرة أخرى:
لاحظ كيف تختفي حالة العداد الثاني تمامًا في اللحظة التي تتوقف فيها عن تصييره. هذا لأنه عندما يزيل React مكونًا، فإنه يدمر حالته.


حذف مكون
عند تحديد خانة الاختيار "عرض العداد الثاني"، يتم تهيئة عداد ثانيCounterوحالته من الصفر (score = 0) وإضافته إلى DOM.


إضافة مكون
يحافظ React على حالة المكون طالما يتم عرضه في موضعه في شجرة واجهة المستخدم.إذا تمت إزالته، أو تم عرض مكون مختلف في نفس الموضع، يتجاهل React حالته.
المكون نفسه في نفس الموضع يحافظ على الحالة
في هذا المثال، هناك علامتان مختلفتان<Counter />:
عند تحديد خانة الاختيار أو مسحها، لا تتم إعادة تعيين حالة العداد. سواء كانت قيمةisFancy trueأمfalse، لديك دائمًا<Counter />كالطفل الأول لعنصرdivالذي تُرجع من المكون الجذريApp:


تحديث حالةAppلا يعيد تعيينCounterلأنCounterيبقى في نفس الموضع
إنه نفس المكون في نفس الموضع، لذا من منظور React، إنه نفس العداد.
مأزق
تذكر أنالموضع في شجرة واجهة المستخدم—وليس في ترميز JSX—هو ما يهم React!هذا المكون لهreturnشرطان مختلفان يحويان علامات<Counter />JSX داخل وخارجif:
قد تتوقع إعادة تعيين الحالة عند تحديد خانة الاختيار، لكن هذا لا يحدث! هذا لأنكلتا علامتي<Counter />هاتين تُعرضان في نفس الموضع.لا يعرف React أين تضع الشروط في دالتك. كل ما "يراه" هو الشجرة التي تُرجعها.
في كلتا الحالتين، يُرجع مكونApp عنصر <div>يحتوي على<Counter />كطفل أول. بالنسبة لـ React، هذان العدادان لهما نفس "العنوان": الطفل الأول للطفل الأول من الجذر. هذه هي الطريقة التي يطابق بها React بينهما في التقديمات السابقة والتالية، بغض النظر عن كيفية هيكلة منطقك.
مكونات مختلفة في نفس الموضع تعيد تعيين الحالة
في هذا المثال، سيؤدي تحديد خانة الاختيار إلى استبدال<Counter>بعنصر<p>:
هنا، تقوم بالتبديل بينأنواعمختلفةمكوناتفي نفس الموضع<div>يحتوي على مكونCounter. ولكن عندما استبدلته بعنصرp، قام React بإزالة مكونCounter من شجرة واجهة المستخدم وتدمير حالته.


عندما يتغيرCounter إلى p، يتم حذفCounterوإضافةp


عند العودة للتبديل، يتم حذفpوإضافةCounter
أيضًا،عندما تقوم بعرض مكون مختلف في نفس الموضع، فإنه يعيد تعيين حالة الشجرة الفرعية بأكملها.لترى كيف يعمل هذا، قم بزيادة العداد ثم حدد خانة الاختيار:
يتم إعادة تعيين حالة العداد عند النقر على خانة الاختيار. على الرغم من أنك تعرض مكونCounter، فإن الطفل الأول لعنصرdivيتغير منsectionإلىdiv. عندما تمت إزالة الطفلsectionمن DOM، تم تدمير الشجرة بأكملها تحته (بما في ذلك مكونCounterوحالته) أيضًا.


عندما يتغيرsection إلى div، يتم حذفsection وإضافة divالجديد


عند العودة للتبديل، يتم حذفdivويتم إضافةsectionالجديد
كقاعدة عامة،إذا كنت تريد الحفاظ على الحالة بين عمليات إعادة التصيير، يجب أن "يتطابق" هيكل شجرتكمن عملية تصيير إلى أخرى. إذا كان الهيكل مختلفًا، يتم تدمير الحالة لأن React تدمر الحالة عند إزالة مكون من الشجرة.
مأزق
لهذا السبب لا يجب عليك تداخل تعريفات دوال المكونات.
هنا، تم تعريف دالة مكونMyTextField داخلMyComponent:
في كل مرة تنقر على الزر، تختفي حالة الإدخال! هذا لأن دالةمختلفةMyTextFieldيتم إنشاؤها لكل عملية تصيير لـMyComponent. أنت تصيّر مكونًامختلفًافي نفس الموضع، لذا يقوم React بإعادة تعيين كل الحالة الموجودة تحته. هذا يؤدي إلى أخطاء ومشاكل في الأداء. لتجنب هذه المشكلة،قم دائمًا بتعريف دوال المكونات في المستوى الأعلى، ولا تتداخل تعريفاتها.
إعادة تعيين الحالة في نفس الموضع
بشكل افتراضي، يحافظ React على حالة المكون بينما يبقى في نفس الموضع. عادةً، هذا بالضبط ما تريده، لذا من المنطقي أن يكون السلوك الافتراضي. لكن في بعض الأحيان، قد ترغب في إعادة تعيين حالة المكون. ضع في اعتبارك هذا التطبيق الذي يسمح للاعبين بتتبع نتائجهما خلال كل دور:
حاليًا، عند تغيير اللاعب، يتم الحفاظ على النتيجة. يظهر المكونانCounterفي نفس الموضع، لذا يرى React أنهمانفسCounterالذي تغيرت خاصيةpersonالخاصة به.
لكن من الناحية المفاهيمية، في هذا التطبيق يجب أن يكونا عدادين منفصلين. قد يظهران في نفس المكان في واجهة المستخدم، لكن أحدهما عداد لتايلور، والآخر عداد لسارة.
هناك طريقتان لإعادة تعيين الحالة عند التبديل بينهما:
- تصيير المكونات في مواضع مختلفة
- إعطاء كل مكون هوية صريحة باستخدام
key
الخيار 1: تصيير مكون في مواضع مختلفة
إذا كنت تريد أن يكون هذان العدادانCounterمستقلين، يمكنك تصييرهما في موضعين مختلفين:
- في البداية،
isPlayerAهيtrue. لذا فإن الموضع الأول يحتوي على حالةCounter، والثاني فارغ. - عند النقر على زر "اللاعب التالي"، يتم مسح الموضع الأول ولكن الموضع الثاني يحتوي الآن على
Counter.


الحالة الابتدائية


النقر على "التالي"


النقر على "التالي" مرة أخرى
يتم تدمير حالة كلCounterفي كل مرة يتم إزالته من DOM. وهذا هو السبب في إعادة تعيينها في كل مرة تنقر فيها على الزر.
هذا الحل مناسب عندما يكون لديك عدد قليل فقط من المكونات المستقلة المعروضة في نفس المكان. في هذا المثال، لديك اثنان فقط، لذا ليس من الصعب عرضهما بشكل منفصل في JSX.
الخيار 2: إعادة تعيين الحالة باستخدام مفتاح
هناك أيضًا طريقة أخرى وأكثر عمومية لإعادة تعيين حالة المكون.
ربما رأيتkeys عندعرض القوائم.المفاتيح ليست فقط للقوائم! يمكنك استخدام المفاتيح لجعل React يميز بين أي مكونات. بشكل افتراضي، يستخدم React الترتيب داخل الأصل ("العداد الأول"، "العداد الثاني") للتمييز بين المكونات. لكن المفاتيح تتيح لك إخبار React بأن هذا ليس مجردأولعداد، أوثانيعداد، بل عداد محدد — على سبيل المثال، عدادتايلور. بهذه الطريقة، ستعرف React عدادتايلورأينما ظهر في الشجرة!
في هذا المثال، لا تشارك العدادان<Counter />الحالة حتى وإن ظهرا في نفس المكان في JSX:
التبديل بين تايلور وسارة لا يحفظ الحالة. هذا لأنلقد أعطيتهمkeys مختلفة:
تحديدkeyيخبر React باستخدامkeyنفسه كجزء من الموضع، بدلاً من ترتيبهم داخل الأصل. هذا هو السبب، حتى وإن عرضتهم في نفس المكان في JSX، يرى React أنهم عدادان مختلفان، وبالتالي لن يشاركا الحالة أبدًا. في كل مرة يظهر عداد على الشاشة، يتم إنشاء حالته. في كل مرة يتم إزالته، يتم تدمير حالته. التبديل بينهما يعيد تعيين حالتهما مرارًا وتكرارًا.
ملاحظة
تذكر أن المفاتيح ليست فريدة عالميًا. إنها تحدد الموضعداخل الأصلفقط.
إعادة تعيين نموذج باستخدام مفتاح
إعادة تعيين الحالة باستخدام مفتاح مفيدة بشكل خاص عند التعامل مع النماذج.
في تطبيق الدردشة هذا، يحتوي مكون<Chat>على حالة إدخال النص:
جرب إدخال شيء في حقل الإدخال، ثم اضغط على "أليس" أو "بوب" لاختيار مستلم مختلف. ستلاحظ أن حالة الإدخال محفوظة لأن<Chat>يتم عرضه في نفس الموضع في الشجرة.
في العديد من التطبيقات، قد يكون هذا هو السلوك المطلوب، ولكن ليس في تطبيق دردشة!لا تريد أن تسمح للمستخدم بإرسال رسالة كتبها بالفعل إلى الشخص الخطأ بسبب نقرة عرضية. لإصلاح ذلك، أضفkey:
هذا يضمن أنه عند اختيار مستلم مختلف، سيتم إعادة إنشاء مكونChatمن الصفر، بما في ذلك أي حالة في الشجرة أسفله. سيقوم React أيضًا بإعادة إنشاء عناصر DOM بدلاً من إعادة استخدامها.
الآن، تبديل المستلم يمسح دائمًا حقل النص:
ملخص
- يحتفظ React بالحالة طالما تم عرض نفس المكون في نفس الموضع.
- الحالة غير مخزنة في علامات JSX. إنها مرتبطة بالموضع في الشجرة الذي وضعت فيه تلك JSX.
- يمكنك إجبار شجرة فرعية على إعادة تعيين حالتها عن طريق إعطائها مفتاحًا مختلفًا.
- لا تضع تعريفات المكونات داخل بعضها البعض، وإلا ستقوم بإعادة تعيين الحالة عن طريق الخطأ.
Try out some challenges
Challenge 1 of 5:Fix disappearing input text #
This example shows a message when you press the button. However, pressing the button also accidentally resets the input. Why does this happen? Fix it so that pressing the button does not reset the input text.
