الحالة كلقطة
قد تبدو متغيرات الحالة مثل متغيرات JavaScript العادية التي يمكنك القراءة منها والكتابة إليها. ومع ذلك، تتصرف الحالة أكثر مثل لقطة. تعيينها لا يغير متغير الحالة الذي لديك بالفعل، بل يؤدي بدلاً من ذلك إلى تشغيل إعادة تصيير.
You will learn
- كيف يؤدي تعيين الحالة إلى تشغيل إعادة التصيير
- متى وكيف تتحدث الحالة
- لماذا لا تتحدث الحالة فورًا بعد تعيينها
- كيف تصل معالجات الأحداث إلى “لقطة” من الحالة
تعيين الحالة يؤدي إلى التصيير
قد تفكر في واجهة المستخدم الخاصة بك على أنها تتغير مباشرة استجابةً لحدث المستخدم مثل النقر. في React، يعمل الأمر بشكل مختلف قليلاً عن هذا النموذج الذهني. في الصفحة السابقة، رأيت أن تعيين الحالة يطلب إعادة تصيير من React. هذا يعني أنه لكي تتفاعل الواجهة مع الحدث، تحتاج إلى تحديث الحالة.
في هذا المثال، عند الضغط على “إرسال”، يخبر setIsSent(true) React بإعادة تصيير واجهة المستخدم:
إليك ما يحدث عند النقر على الزر:
- ينفذ معالج الحدث
onSubmit. setIsSent(true)يعينisSentإلىtrueويضع تصييرًا جديدًا في قائمة الانتظار.- يعيد React تصيير المكون وفقًا لقيمة
isSentالجديدة.
دعنا نلقي نظرة فاحصة على العلاقة بين الحالة والتصيير.
التصيير يأخذ لقطة في الوقت
“التصيير” يعني أن React يستدعي مكونك، وهو دالة. الـ JSX الذي ترجعه من تلك الدالة يشبه لقطة من واجهة المستخدم في الوقت. تم حساب props ومعالجات الأحداث والمتغيرات المحلية باستخدام حالته في وقت التصيير.
على عكس الصورة الفوتوغرافية أو إطار الفيلم، “لقطة” واجهة المستخدم التي ترجعها تفاعلية. إنها تتضمن منطقًا مثل معالجات الأحداث التي تحدد ما يحدث استجابة للمدخلات. يحدّث React الشاشة لتطابق هذه اللقطة ويربط معالجات الأحداث. ونتيجة لذلك، سيؤدي الضغط على زر إلى تشغيل معالج النقر من JSX الخاص بك.
عندما يعيد React تصيير مكون:
- يستدعي React دالتك مرة أخرى.
- ترجع دالتك لقطة JSX جديدة.
- ثم يحدّث React الشاشة لتطابق اللقطة التي أرجعتها دالتك.

React ينفذ الدالة 
حساب اللقطة 
تحديث شجرة DOM
Illustrated by Rachel Lee Nabors
كذاكرة المكون، الحالة ليست مثل متغير عادي يختفي بعد عودة دالتك. الحالة في الواقع “تعيش” في React نفسه - كما لو كانت على رف! - خارج دالتك. عندما يستدعي React مكونك، يعطيك لقطة من الحالة لذلك التصيير المحدد. يرجع مكونك لقطة من واجهة المستخدم مع مجموعة جديدة من props ومعالجات الأحداث في JSX الخاص به، وكلها محسوبة باستخدام قيم الحالة من ذلك التصيير!

أنت تخبر React بتحديث الحالة 
React يحدّث قيمة الحالة 
React يمرر لقطة من قيمة الحالة إلى المكون
Illustrated by Rachel Lee Nabors
إليك تجربة صغيرة لتوضيح كيف يعمل هذا. في هذا المثال، قد تتوقع أن النقر على زر “+3” سيزيد العداد ثلاث مرات لأنه يستدعي setNumber(number + 1) ثلاث مرات.
انظر ماذا يحدث عند النقر على زر “+3”:
لاحظ أن number يزداد مرة واحدة فقط لكل نقرة!
تعيين الحالة يغيرها فقط للتصيير التالي. خلال التصيير الأول، كانت number هي 0. هذا هو السبب في أن قيمة number في معالج onClick لذلك التصيير لا تزال 0 حتى بعد استدعاء setNumber(number + 1):
<button onClick={() => { setNumber(number + 1); setNumber(number + 1); setNumber(number + 1); }}>+3</button>
إليك ما يخبر معالج النقر لهذا الزر 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 لهذا التصيير، يبدو معالج الحدث الخاص به كما يلي:
<button onClick={() => { setNumber(0 + 1); setNumber(0 + 1); setNumber(0 + 1); }}>+3</button>
بالنسبة للتصيير التالي، number هي 1، لذا يبدو معالج النقر لذلك التصيير كما يلي:
<button onClick={() => { setNumber(1 + 1); setNumber(1 + 1); setNumber(1 + 1); }}>+3</button>
هذا هو السبب في أن النقر على الزر مرة أخرى سيعين العداد إلى 2، ثم إلى 3 عند النقر التالي، وهكذا.
الحالة عبر الوقت
حسنًا، كان ذلك ممتعًا. حاول تخمين ما سيعرضه التنبيه عند النقر على هذا الزر:
إذا استخدمت طريقة الاستبدال من قبل، يمكنك تخمين أن التنبيه يعرض “0”:
setNumber(0 + 5); alert(0);
ولكن ماذا لو وضعت مؤقتًا على التنبيه، بحيث يُطلق فقط بعد إعادة تصيير المكون؟ هل سيقول “0” أو “5”؟ خمّن!
متفاجئ؟ إذا استخدمت طريقة الاستبدال، يمكنك رؤية “لقطة” الحالة الممررة إلى التنبيه.
setNumber(0 + 5); setTimeout(() => { alert(0); }, 3000);
قد تكون الحالة المخزنة في React قد تغيرت بحلول الوقت الذي يتم فيه تشغيل التنبيه، لكنها تم جدولتها باستخدام لقطة من الحالة في الوقت الذي تفاعل فيه المستخدم معها!
قيمة متغير الحالة لا تتغير أبدًا داخل التصيير، حتى لو كان كود معالج الحدث الخاص به غير متزامن. داخل onClick لذلك التصيير، تظل قيمة number 0 حتى بعد استدعاء setNumber(number + 5). تم “تثبيت” قيمتها عندما “أخذ” React “اللقطة” من واجهة المستخدم من خلال استدعاء مكونك.
إليك مثال على كيف يجعل ذلك معالجات الأحداث الخاصة بك أقل عرضة لأخطاء التوقيت. فيما يلي نموذج يرسل رسالة مع تأخير خمس ثوانٍ. تخيل هذا السيناريو:
- تضغط على زر “إرسال”، مرسلاً “مرحبًا” إلى Alice.
- قبل انتهاء تأخير الخمس ثوانٍ، تغير قيمة حقل “إلى” إلى “Bob”.
ماذا تتوقع أن يعرض alert؟ هل سيعرض، “قلت مرحبًا إلى Alice”؟ أم سيعرض، “قلت مرحبًا إلى Bob”؟ قم بالتخمين بناءً على ما تعرفه، ثم جربه:
يحتفظ React بقيم الحالة “ثابتة” داخل معالجات الأحداث لتصيير واحد. لست بحاجة إلى القلق بشأن ما إذا كانت الحالة قد تغيرت أثناء تشغيل الكود.
ولكن ماذا لو أردت قراءة أحدث حالة قبل إعادة التصيير؟ سترغب في استخدام دالة محدّث الحالة، المشروحة في الصفحة التالية!
خلاصة
- تعيين الحالة يطلب تصييرًا جديدًا.
- يخزن React الحالة خارج مكونك، كما لو كانت على رف.
- عندما تستدعي
useState، يعطيك React لقطة من الحالة لذلك التصيير. - المتغيرات ومعالجات الأحداث لا “تنجو” من إعادات التصيير. كل تصيير له معالجات الأحداث الخاصة به.
- كل تصيير (والدوال بداخله) سيرى دائمًا “لقطة” الحالة التي أعطاها React لذلك التصيير.
- يمكنك استبدال الحالة ذهنيًا في معالجات الأحداث، بشكل مماثل لكيفية تفكيرك في JSX المُصَيَّر.
- معالجات الأحداث المنشأة في الماضي لديها قيم الحالة من التصيير الذي تم إنشاؤها فيه.
تحدي 1 من 1: تنفيذ إشارة مرور
إليك مكون إشارة عبور المشاة الذي يتبدل عند الضغط على الزر:
أضف alert إلى معالج النقر. عندما تكون الإشارة خضراء وتقول “Walk”، يجب أن يقول النقر على الزر “Stop is next”. عندما تكون الإشارة حمراء وتقول “Stop”، يجب أن يقول النقر على الزر “Walk is next”.
هل يحدث فرق ما إذا وضعت alert قبل أو بعد استدعاء setWalk؟