الحالة معزولة بين المكونات. يتتبع React أي حالة تنتمي إلى أي مكون بناءً على مكانهم في شجرة واجهة المستخدم. يمكنك التحكم في متى يتم الحفاظ على الحالة ومتى يتم إعادة تعيينها بين عمليات إعادة العرض.
You will learn
متى يختار React الحفاظ على الحالة أو إعادة تعيينها
كيفية إجبار React على إعادة تعيين حالة المكون
كيف تؤثر المفاتيح والأنواع على ما إذا كان سيتم الحفاظ على الحالة
الحالة مرتبطة بموضع في شجرة العرض
يبني React أشجار العرض لبنية المكونات في واجهة المستخدم الخاصة بك.
عندما تعطي مكونًا حالة، قد تعتقد أن الحالة “تعيش” داخل المكون. لكن الحالة في الواقع محفوظة داخل React. يربط React كل جزء من الحالة التي يحتفظ بها مع المكون الصحيح حسب مكان جلوس ذلك المكون في شجرة العرض.
هنا، يوجد وسم <Counter /> JSX واحد فقط، لكنه يتم عرضه في موضعين مختلفين:
هذه عدّادان منفصلان لأن كل واحد يتم عرضه في موضعه الخاص في الشجرة. عادةً لا تحتاج إلى التفكير في هذه المواضع لاستخدام React، لكن قد يكون من المفيد فهم كيفية عملها.
في React، كل مكون على الشاشة له حالة معزولة تمامًا. على سبيل المثال، إذا عرضت مكونَي Counter جنبًا إلى جنب، فإن كلًا منهما سيحصل على حالاته الخاصة والمستقلة score و hover.
جرب النقر على كلا العدّادين ولاحظ أنهما لا يؤثران على بعضهما البعض:
As you can see, when one counter is updated, only the state for that component is updated:
تحديث الحالة
سيحتفظ React بالحالة طالما أنك تعرض نفس المكون في نفس الموضع في الشجرة. لرؤية ذلك، قم بزيادة كلا العدّادين، ثم أزل المكون الثاني بإلغاء تحديد مربع الاختيار “Render the second 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
أيضًا، عندما تعرض مكونًا مختلفًا في نفس الموضع، فإنه يعيد تعيين حالة الشجرة الفرعية بأكملها. لرؤية كيفية عمل ذلك، قم بزيادة العدّاد ثم حدد مربع الاختيار:
The counter state gets reset when you click the checkbox. Although you render a Counter, the first child of the div changes from a div to a section. When the child div was removed from the DOM, the whole tree below it (including the Counter and its state) was destroyed as well.
When section changes to div, the section is deleted and the new div is added
When switching back, the div is deleted and the new section is added
كقاعدة عامة، إذا كنت تريد الحفاظ على الحالة بين عمليات إعادة العرض، فإن بنية شجرتك يجب أن “تتطابق” من عرض إلى آخر. إذا كانت البنية مختلفة، يتم تدمير الحالة لأن React يدمر الحالة عندما يزيل مكونًا من الشجرة.
مأزق
هذا هو السبب في أنه يجب عدم تداخل تعريفات دوال المكونات.
هنا، يتم تعريف دالة مكون MyTextFieldداخلMyComponent:
في كل مرة تنقر فيها على الزر، تختفي حالة الإدخال! هذا لأنه يتم إنشاء دالة MyTextFieldمختلفة لكل عرض من MyComponent. أنت تعرض مكونًا مختلفًا في نفس الموضع، لذلك يعيد React تعيين كل الحالة أدناه. يؤدي هذا إلى أخطاء ومشاكل في الأداء. لتجنب هذه المشكلة، قم دائمًا بالإعلان عن دوال المكونات في المستوى الأعلى، ولا تداخل تعريفاتها.
إعادة تعيين الحالة في نفس الموضع
بشكل افتراضي، يحفظ React حالة مكون ما بينما يبقى في نفس الموضع. عادةً، هذا بالضبط ما تريده، لذا فهو منطقي كسلوك افتراضي. لكن في بعض الأحيان، قد ترغب في إعادة تعيين حالة المكون. فكر في هذا التطبيق الذي يتيح لاعبين اثنين تتبع نقاطهما خلال كل دور:
حاليًا، عندما تغيّر اللاعب، يتم الحفاظ على النتيجة. يظهر Counterان في نفس الموضع، لذلك يراهما React على أنهما نفسCounter الذي تغير prop person الخاص به.
لكن من الناحية المفاهيمية، في هذا التطبيق يجب أن يكونا عدّادين منفصلين. قد يظهران في نفس المكان في واجهة المستخدم، لكن أحدهما عدّاد لـ Taylor، والآخر عدّاد لـ Sarah.
هناك طريقتان لإعادة تعيين الحالة عند التبديل بينهما:
عرض المكونات في مواضع مختلفة
إعطاء كل مكون هوية صريحة باستخدام key
الخيار 1: عرض مكون في مواضع مختلفة
إذا كنت تريد أن يكون هذان Counterان مستقلين، يمكنك عرضهما في موضعين مختلفين:
Initially, isPlayerA is true. So the first position contains Counter state, and the second one is empty.
When you click the “Next player” button the first position clears but the second one now contains a Counter.
الحالة الأولية
النقر على “next”
النقر على “next” مرة أخرى
يتم تدمير حالة كل Counter في كل مرة تتم إزالته من DOM. هذا هو السبب في أنهم يعيدون التعيين في كل مرة تنقر فيها على الزر.
هذا الحل مناسب عندما يكون لديك عدد قليل فقط من المكونات المستقلة المعروضة في نفس المكان. في هذا المثال، لديك اثنان فقط، لذلك ليس من المزعج عرض كليهما بشكل منفصل في JSX.
الخيار 2: إعادة تعيين الحالة باستخدام key
هناك أيضًا طريقة أخرى أكثر عمومية لإعادة تعيين حالة المكون.
ربما رأيت keys عند عرض القوائم. المفاتيح ليست للقوائم فقط! يمكنك استخدام المفاتيح لجعل React يميز بين أي مكونات. بشكل افتراضي، يستخدم React الترتيب داخل الأب (“العدّاد الأول”، “العدّاد الثاني”) للتمييز بين المكونات. لكن المفاتيح تتيح لك إخبار React أن هذا ليس مجرد عدّاد أول، أو عدّاد ثانٍ، بل عدّاد محدد—على سبيل المثال، عدّاد Taylor. بهذه الطريقة، سيعرف React عدّاد Taylor أينما ظهر في الشجرة!
في هذا المثال، <Counter />ان لا يشتركان في الحالة رغم أنهما يظهران في نفس المكان في JSX:
تحديد key يخبر React باستخدام key نفسه كجزء من الموضع، بدلاً من ترتيبه داخل الأب. هذا هو السبب في أنه على الرغم من أنك تعرضهما في نفس المكان في JSX، يراهما React كعدّادين مختلفين، وبالتالي لن يشتركا أبدًا في الحالة. في كل مرة يظهر فيها عدّاد على الشاشة، يتم إنشاء حالته. في كل مرة تتم إزالته، يتم تدمير حالته. التبديل بينهما يعيد تعيين حالتهما مرارًا وتكرارًا.
ملاحظة
تذكر أن المفاتيح ليست فريدة عالميًا. إنها تحدد الموضع داخل الأب فقط.
إعادة تعيين نموذج باستخدام key
إعادة تعيين الحالة باستخدام key مفيدة بشكل خاص عند التعامل مع النماذج.
في تطبيق الدردشة هذا، يحتوي مكون <Chat> على حالة إدخال النص:
جرب إدخال شيء ما في الإدخال، ثم اضغط على “Alice” أو “Bob” لاختيار مستلم مختلف. ستلاحظ أن حالة الإدخال محفوظة لأن <Chat> يتم عرضه في نفس الموضع في الشجرة.
في العديد من التطبيقات، قد يكون هذا هو السلوك المطلوب، ولكن ليس في تطبيق دردشة! أنت لا تريد السماح للمستخدم بإرسال رسالة كتبها بالفعل إلى شخص خاطئ بسبب نقرة عرضية. لإصلاح ذلك، أضف key:
<Chat key={to.id} contact={to} />
هذا يضمن أنه عندما تحدد مستلمًا مختلفًا، سيتم إعادة إنشاء مكون Chat من الصفر، بما في ذلك أي حالة في الشجرة أدناه. سيعيد React أيضًا إنشاء عناصر DOM بدلاً من إعادة استخدامها.
في تطبيق دردشة حقيقي، من المحتمل أن ترغب في استرداد حالة الإدخال عندما يحدد المستخدم المستلم السابق مرة أخرى. هناك عدة طرق للحفاظ على الحالة “حية” لمكون لم يعد مرئيًا:
يمكنك عرض جميع المحادثات بدلاً من المحادثة الحالية فقط، لكن إخفاء جميع المحادثات الأخرى باستخدام CSS. لن تتم إزالة المحادثات من الشجرة، لذلك سيتم الحفاظ على حالتها المحلية. يعمل هذا الحل بشكل رائع لواجهات المستخدم البسيطة. لكن يمكن أن يصبح بطيئًا جدًا إذا كانت الأشجار المخفية كبيرة وتحتوي على الكثير من عقد DOM.
يمكنك رفع الحالة والاحتفاظ بالرسالة المعلقة لكل مستلم في المكون الأب. بهذه الطريقة، عندما تتم إزالة المكونات الأبناء، لا يهم، لأن الأب هو الذي يحتفظ بالمعلومات المهمة. هذا هو الحل الأكثر شيوعًا.
يمكنك أيضًا استخدام مصدر مختلف بالإضافة إلى حالة React. على سبيل المثال، من المحتمل أن ترغب في استمرار مسودة الرسالة حتى لو أغلق المستخدم الصفحة عن طريق الخطأ. لتنفيذ ذلك، يمكنك جعل مكون Chat يهيئ حالته بالقراءة من localStorage، وحفظ المسودات هناك أيضًا.
بغض النظر عن الاستراتيجية التي تختارها، فإن محادثة مع Alice تختلف مفاهيميًا عن محادثة مع Bob، لذا من المنطقي إعطاء key لشجرة <Chat> بناءً على المستلم الحالي.
خلاصة
يحتفظ React بالحالة طالما يتم عرض نفس المكون في نفس الموضع.
لا يتم الاحتفاظ بالحالة في وسوم JSX. إنها مرتبطة بموضع الشجرة الذي تضع فيه ذلك JSX.
يمكنك إجبار شجرة فرعية على إعادة تعيين حالتها بإعطائها مفتاحًا مختلفًا.
لا تداخل تعريفات المكونات، وإلا ستعيد تعيين الحالة عن طريق الخطأ.
جرّب بعض التحديات
تحدي 1 من 5:
أصلح نص الإدخال المختفي
يعرض هذا المثال رسالة عند الضغط على الزر. ومع ذلك، فإن الضغط على الزر يعيد تعيين الإدخال عن طريق الخطأ أيضًا. لماذا يحدث هذا؟ أصلحه بحيث لا يؤدي الضغط على الزر إلى إعادة تعيين نص الإدخال.