الحالة كصورة لحظية
قد تبدو متغيرات الحالة مثل متغيرات جافا سكريبت العادية التي يمكنك قراءتها والكتابة إليها. ومع ذلك، تتصرف الحالة بشكل أشبه بلقطة. تعيينها لا يغير متغير الحالة الذي لديك بالفعل، بل يؤدي بدلاً من ذلك إلى إعادة التصيير.
سوف تتعلم
- كيف يؤدي تعيين الحالة إلى إعادة التصيير
- متى وكيف يتم تحديث الحالة
- لماذا لا يتم تحديث الحالة فورًا بعد تعيينها
- كيف تصل معالجات الأحداث إلى "لقطة" من الحالة
تعيين الحالة يؤدي إلى التصيير
قد تعتقد أن واجهة المستخدم الخاصة بك تتغير مباشرة استجابةً لحدث المستخدم مثل النقر. في React، الأمر يعمل بشكل مختلف قليلاً عن هذا النموذج الذهني. في الصفحة السابقة، رأيت أنتعيين الحالة يطلب إعادة تصييرمن React. وهذا يعني لكي تتفاعل الواجهة مع الحدث، تحتاج إلىتحديث الحالة.
في هذا المثال، عند الضغط على "إرسال"، فإنsetIsSent(true)تخبر React بإعادة تصيير واجهة المستخدم:
إليك ما يحدث عند النقر على الزر:
- يتم تنفيذ معالج الحدث
onSubmit. setIsSent(true)تعيّنisSentإلىtrueوتضيف تصييرًا جديدًا إلى قائمة الانتظار.- يعيد React تصيير المكون وفقًا لقيمة
isSentالجديدة.
دعنا نلقي نظرة أقرب على العلاقة بين الحالة والتصيير.
التصيير يأخذ لقطة في الوقت
"التصيير"يعني أن React تستدعي مكونك، وهو دالة. JSX الذي تُرجعه من تلك الدالة يشبه لقطة لواجهة المستخدم في وقت محدد. خصائصه ومعالجات أحداثه ومتغيراته المحلية جميعها تم حسابهاباستخدام حالته في وقت التصيير.
على عكس الصورة الفوتوغرافية أو إطار الفيلم، فإن "لقطة" واجهة المستخدم التي تُرجعها تكون تفاعلية. فهي تتضمن منطقًا مثل معالجي الأحداث التي تحدد ما يحدث استجابةً للمدخلات. يقوم React بتحديث الشاشة لتطابق هذه اللقطة ويربط معالجي الأحداث. نتيجةً لذلك، سيؤدي الضغط على زر إلى تشغيل معالج النقر من JSX الخاص بك.
عندما يعيد React عرض مكون:
- يقوم React باستدعاء دالتك مرة أخرى.
- تُرجع دالتك لقطة JSX جديدة.
- ثم يقوم React بتحديث الشاشة لتطابق اللقطة التي أرجعتْها دالتك.
React يقوم بتنفيذ الدالة
حساب اللقطة
تحديث شجرة DOM
الرسوم التوضيحية منRachel Lee Nabors
بصفته ذاكرة المكون، فإن الحالة ليست مثل متغير عادي يختفي بعد إرجاع دالتك. الحالة في الواقع "تعيش" في React نفسه - كما لو كانت على رف! - خارج دالتك. عندما يستدعي React مكونك، فإنه يمنحك لقطة من الحالة لذلك العرض المحدد. يُرجع مكونك لقطة لواجهة المستخدم مع مجموعة جديدة من الخصائص ومعالجي الأحداث في JSX الخاص به، وكلها محسوبةباستخدام قيم الحالة من ذلك العرض!
أنت تخبر React بتحديث الحالة
React يقوم بتحديث قيمة الحالة
React يمرر لقطة من قيمة الحالة إلى المكون
الرسوم التوضيحية منRachel Lee Nabors
إليك تجربة صغيرة لتوضح لك كيف يعمل هذا. في هذا المثال، قد تتوقع أن يؤدي النقر على زر "+3" إلى زيادة العداد ثلاث مرات لأنه يستدعيsetNumber(number + 1)ثلاث مرات.
شاهد ما يحدث عند النقر على زر "+3":
لاحظ أنnumberيزداد مرة واحدة فقط لكل نقرة!
تعيين الحالة يغيرها فقط للـتالي.أثناء التصيير الأول، كانت قيمةnumberهي0. لهذا السبب، في معالجذلك التصييرonClick، تظل قيمةnumber هي 0حتى بعد استدعاءsetNumber(number + 1):
إليك ما يخبره معالج النقر لهذا الزر React أن يفعله:
setNumber(number + 1):numberهي0لذاsetNumber(0 + 1).- يستعد React لتغيير
numberإلى1في التصيير التالي.
- يستعد React لتغيير
setNumber(number + 1):numberهي0لذاsetNumber(0 + 1).- يستعد React لتغيير
numberإلى1في التصيير التالي.
- يستعد React لتغيير
setNumber(number + 1):numberهي0لذاsetNumber(0 + 1).- يستعد React لتغيير
numberإلى1في التصيير التالي.
- يستعد React لتغيير
على الرغم من أنك استدعيتsetNumber(number + 1)ثلاث مرات، في معالج الحدثلهذا التصيير، تظل قيمةnumberدائمًا0، لذا أنت تعين الحالة إلى1ثلاث مرات. هذا هو السبب، بعد انتهاء معالج الحدث، يعيد React تصيير المكون بقيمةnumberتساوي1بدلاً من3.
يمكنك أيضًا تصور ذلك عن طريق استبدال متغيرات الحالة ذهنيًا بقيمها في الكود الخاص بك. بما أن متغير الحالةnumber هو 0لـهذا التصيير، فإن معالج الحدث الخاص به يبدو هكذا:
للتصيير التالي، تكون قيمةnumberهي1، لذا فإن معالج النقرلذلك التصييريبدو هكذا:
هذا هو السبب في أن النقر على الزر مرة أخرى سيضبط العداد إلى2، ثم إلى3في النقرة التالية، وهكذا.
الحالة عبر الزمن
حسنًا، كان ذلك ممتعًا. حاول تخمين ما سيعرضه النقر على هذا الزر:
إذا استخدمت طريقة الاستبدال من قبل، يمكنك تخمين أن التنبيه يظهر "0":
لكن ماذا لو وضعت مؤقتًا على التنبيه، بحيث يتم تشغيلهبعدإعادة عرض المكون؟ هل سيقول "0" أم "5"؟ خمّن!
متفاجئ؟ إذا استخدمت طريقة الاستبدال، يمكنك رؤية "اللقطة" للحالة التي تم تمريرها إلى التنبيه.
قد تكون الحالة المخزنة في React قد تغيرت بحلول وقت تشغيل التنبيه، لكنه تم جدولته باستخدام لقطة للحالة في الوقت الذي تفاعل فيه المستخدم معها!
قيمة متغير الحالة لا تتغير أبدًا داخل عملية عرض واحدة،حتى لو كان كود معالج الأحداث غير متزامن. داخلمعالجonClickلتلك العملية، تظل قيمةnumber 0حتى بعد استدعاءsetNumber(number + 5). تم "تثبيت" قيمتها عندما "أخذت React لقطة" للواجهة عن طريق استدعاء مكونك.
إليك مثال على كيف يجعل ذلك معالجات الأحداث الخاصة بك أقل عرضة لأخطاء التوقيت. أدناه نموذج يرسل رسالة بتأخير خمس ثوانٍ. تخيل هذا السيناريو:
- تضغط على زر "إرسال"، مرسلاً "مرحبًا" إلى أليس.
- قبل انتهاء التأخير البالغ خمس ثوانٍ، تغير قيمة حقل "إلى" إلى "بوب".
ماذا تتوقع أن يعرضalert؟ هل سيعرض، "قلت مرحبًا لأليس"؟ أم سيعرض، "قلت مرحبًا لبوب"؟ خمّن بناءً على ما تعرفه، ثم جربه:
يحافظ React على قيم الحالة "مثبتة" داخل معالجات الأحداث لعملية عرض واحدة.لا داعي للقلق بشأن ما إذا كانت الحالة قد تغيرت أثناء تشغيل الكود.
لكن ماذا لو أردت قراءة أحدث حالة قبل إعادة العرض؟ ستحتاج إلى استخدامدالة تحديث الحالة، والتي سيتم تناولها في الصفحة التالية!
ملخص
- تعيين الحالة يطلب عرضًا جديدًا.
- يخزن React الحالة خارج مكونك، كما لو كانت على رف.
- عند استدعائك
useState، يعطيك React لقطة للحالةلتلك العملية. - المتغيرات ومعالجات الأحداث لا "تبقى" عبر عمليات إعادة العرض. كل عملية عرض لها معالجات الأحداث الخاصة بها.
- كل عملية عرض (والدوال داخلها) ستشاهد دائمًا لقطة الحالة التي أعطاها Reactلتلكالعملية.
- يمكنك استبدال الحالة ذهنيًا في معالجات الأحداث، بشكل مشابه لكيفية تفكيرك في JSX المعروض.
- معالجات الأحداث التي تم إنشاؤها في الماضي لها قيم الحالة من العملية التي تم إنشاؤها فيها.
جرب بعض التحديات
Challenge 1 of 1:Implement a traffic light #
Here is a crosswalk light component that toggles when the button is pressed:
Add an alert to the click handler. When the light is green and says “Walk”, clicking the button should say “Stop is next”. When the light is red and says “Stop”, clicking the button should say “Walk is next”.
Does it make a difference whether you put the alert before or after the setWalk call?
