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

الرسم التوضيحي بواسطةRachel Lee Nabors
إنهم لا يعرفون إلى أين تريد الذهاب، إنهم فقط يتبعون أوامرك. (وإذا أخطأت في الاتجاهات، ينتهي بك الأمر في المكان الخطأ!) يُطلق عليهاأمريةلأن عليك "أمر" كل عنصر، من مؤشر التحميل إلى الزر، بإخبار الكمبيوتركيفيةتحديث واجهة المستخدم.
في هذا المثال لبرمجة واجهة المستخدم الأمرية، تم بناء النموذجبدوناستخدام React. إنه يستخدم فقطDOMللمتصفح:
يعمل التلاعب بواجهة المستخدم بشكل إجرائي بشكل جيد بما يكفي للأمثلة المعزولة، لكن إدارته تصبح أكثر صعوبة بشكل كبير في الأنظمة الأكثر تعقيدًا. تخيل تحديث صفحة مليئة بنماذج مختلفة مثل هذا. إضافة عنصر واجهة مستخدم جديد أو تفاعل جديد سيتطلب التحقق بعناية من جميع الأكواد الموجودة للتأكد من أنك لم تقدم خطأ (على سبيل المثال، نسيان إظهار أو إخفاء شيء ما).
تم بناء React لحل هذه المشكلة.
في React، لا تتلاعب بواجهة المستخدم مباشرة — أي أنك لا تفعل أو تعطل أو تظهر أو تخفي المكونات مباشرة. بدلاً من ذلك،تعلن عما تريد إظهاره،وتكتشف React كيفية تحديث واجهة المستخدم. فكر في ركوب سيارة أجرة وإخبار السائق بالمكان الذي تريد الذهاب إليه بدلاً من إخباره بالضبط أين ينعطف. إن مهمة السائق هي أن يوصلَك إلى هناك، وقد يعرف حتى بعض الطرق المختصرة التي لم تفكر فيها!

الرسم التوضيحي بواسطةRachel Lee Nabors
التفكير في واجهة المستخدم بشكل إعلاني
لقد رأيت كيفية تنفيذ نموذج بشكل إجرائي أعلاه. لفهم كيفية التفكير في React بشكل أفضل، ستتخطى إعادة تنفيذ واجهة المستخدم هذه في React أدناه:
- حددالحالات المرئية المختلفة لمكونك
- حددما الذي يُحفز تغييرات تلك الحالات
- مثلالحالة في الذاكرة باستخدام
useState - أزلأي متغيرات حالة غير أساسية
- اربطمعالجات الأحداث لتعيين الحالة
الخطوة 1: حدد الحالات المرئية المختلفة لمكونك
في علوم الحاسوب، قد تسمع عن"آلة حالة"تكون في واحدة من عدة "حالات". إذا كنت تعمل مع مصمم، فقد تكون رأيت نماذج تجريبية لـ "حالات مرئية" مختلفة. تقف React عند تقاطع التصميم وعلوم الحاسوب، لذا فإن كلا الفكرتين هما مصدر إلهام.
أولاً، تحتاج إلى تصور جميع "الحالات" المختلفة لواجهة المستخدم التي قد يراها المستخدم:
- فارغة: النموذج يحتوي على زر "إرسال" معطل.
- كتابة: النموذج يحتوي على زر "إرسال" مفعل.
- إرسال: النموذج معطل بالكامل. يتم عرض مؤشر التحميل.
- نجاح: يتم عرض رسالة "شكرًا لك" بدلاً من النموذج.
- خطأ: نفس حالة الكتابة، ولكن مع رسالة خطأ إضافية.
تمامًا مثل المصمم، سترغب في "إنشاء نموذج تجريبي" أو "نماذج تجريبية" للحالات المختلفة قبل إضافة المنطق. على سبيل المثال، إليك نموذج تجريبي للجزء المرئي فقط من النموذج. يتم التحكم في هذا النموذج التجريبي بواسطة خاصية تسمىstatusبقيمة افتراضية هي'empty':
يمكنك تسمية تلك الخاصية بأي شيء تريده، فالتسمية ليست مهمة. حاول تعديلstatus = 'empty'إلىstatus = 'success'لترى رسالة النجاح تظهر. يتيح لك النمذجة التجريبية التكرار بسرعة على واجهة المستخدم قبل توصيل أي منطق. إليك نموذج أولي أكثر تفصيلاً لنفس المكون، لا يزال "متحكمًا به" بواسطة خاصيةstatus:
الخطوة 2: حدد ما الذي يُحفز تغييرات تلك الحالات
يمكنك تحفيز تحديثات الحالة استجابة لنوعين من المدخلات:
- مدخلات بشرية،مثل النقر على زر، الكتابة في حقل، التنقل عبر رابط.
- مدخلات حاسوبية،مثل وصول استجابة شبكة، اكتمال مهلة زمنية، تحميل صورة.


رسم توضيحي بواسطةRachel Lee Nabors
في كلتا الحالتين،يجب عليك تعيينمتغيرات الحالةلتحديث واجهة المستخدم.بالنسبة للنموذج الذي تقوم بتطويره، ستحتاج إلى تغيير الحالة استجابةً لعدة مدخلات مختلفة:
- تغيير إدخال النص(من قبل المستخدم) يجب أن يحوله من حالةفارغإلى حالةكتابةأو العكس، اعتمادًا على ما إذا كان مربع النص فارغًا أم لا.
- النقر على زر الإرسال(من قبل المستخدم) يجب أن يحوله إلى حالةجاري الإرسال.
- استجابة الشبكة الناجحة(من الكمبيوتر) يجب أن تحوله إلى حالةنجاح.
- استجابة الشبكة الفاشلة(من الكمبيوتر) يجب أن تحوله إلى حالةخطأمع رسالة الخطأ المطابقة.
ملاحظة
لاحظ أن مدخلات المستخدم غالبًا ما تتطلبمعالجات الأحداث!
للمساعدة في تصور هذا التدفق، حاول رسم كل حالة على الورق كدائرة موسومة، وكل تغيير بين حالتين كسهم. يمكنك رسم العديد من التدفقات بهذه الطريقة واكتشاف الأخطاء قبل التنفيذ بوقت طويل.


حالات النموذج
الخطوة 3: تمثيل الحالة في الذاكرة باستخدامuseState
بعد ذلك، ستحتاج إلى تمثيل الحالات المرئية للمكون الخاص بك في الذاكرة باستخدامuseState.البساطة هي المفتاح: كل جزء من الحالة هو "قطعة متحركة"، وتريد أقل عدد ممكن من "القطع المتحركة".المزيد من التعقيد يؤدي إلى المزيد من الأخطاء!
ابدأ بالحالة التييجبأن تكون هناك حتمًا. على سبيل المثال، ستحتاج إلى تخزينanswerللإدخال، وerror(إذا وجدت) لتخزين آخر خطأ:
بعد ذلك، ستحتاج إلى متغير حالة يمثل أيًا من الحالات المرئية التي تريد عرضها. عادةً ما يكون هناك أكثر من طريقة واحدة لتمثيل ذلك في الذاكرة، لذا ستحتاج إلى التجربة.
إذا كنت تواجه صعوبة في التفكير في أفضل طريقة على الفور، ابدأ بإضافة ما يكفي من الحالة بحيث تكونمتأكدًا تمامًامن تغطية جميع الحالات المرئية الممكنة:
من المحتمل ألا تكون فكرتك الأولى هي الأفضل، لكن لا بأس في ذلك—إعادة هيكلة الحالة هي جزء من العملية!
الخطوة 4: إزالة أي متغيرات حالة غير أساسية
أنت تريد تجنب التكرار في محتوى الحالة بحيث تتعقب فقط ما هو أساسي. قضاء القليل من الوقت في إعادة هيكلة بنية حالتك سيجعل مكوناتك أسهل في الفهم، ويقلل التكرار، ويتجنب المعاني غير المقصودة. هدفك هومنع الحالات التي لا تمثل فيها الحالة في الذاكرة أي واجهة مستخدم صالحة تريد أن يراها المستخدم.(على سبيل المثال، لا تريد أبدًا عرض رسالة خطأ وتعطيل الإدخال في نفس الوقت، وإلا لن يتمكن المستخدم من تصحيح الخطأ!)
فيما يلي بعض الأسئلة التي يمكنك طرحها حول متغيرات حالتك:
- هل تسبب هذه الحالة تناقضًا؟على سبيل المثال، لا يمكن أن يكون كل من
isTypingوisSubmittingبقيمةtrue. يشير التناقض عادةً إلى أن الحالة غير مقيدة بما يكفي. هناك أربع تركيبات محتملة لقيمتين منطقيتين، لكن ثلاث منها فقط تتوافق مع حالات صالحة. لإزالة الحالة "المستحيلة"، يمكنك دمجها فيstatusيجب أن تكون واحدة من ثلاث قيم:'typing'، أو'submitting'، أو'success'. - هل تتوفر نفس المعلومات في متغير حالة آخر بالفعل؟تناقض آخر: لا يمكن أن يكون كل من
isEmptyوisTypingبقيمةtrueفي نفس الوقت. بجعلهما متغيري حالة منفصلين، تخاطر بحدوث عدم تزامن بينهما مما يتسبب في أخطاء. لحسن الحظ، يمكنك إزالةisEmptyوالتحقق بدلاً من ذلك منanswer.length === 0. - هل يمكنك الحصول على نفس المعلومات من عكس متغير حالة آخر؟
isErrorغير ضروري لأنه يمكنك التحقق منerror !== nullبدلاً من ذلك.
بعد هذا التنظيف، يتبقى لديك 3 (من أصل 7!) متغيرات حالةأساسية:
أنت تعلم أنها أساسية، لأنه لا يمكنك إزالة أي منها دون كسر الوظيفة.
الخطوة 5: ربط معالجات الأحداث بتعيين الحالة
أخيرًا، أنشئ معالجات الأحداث التي تقوم بتحديث الحالة. فيما يلي النموذج النهائي، مع ربط جميع معالجات الأحداث:
على الرغم من أن هذا الرمز أطول من المثال الإلزامي الأصلي، إلا أنه أقل هشاشة بكثير. التعبير عن جميع التفاعلات كتغييرات في الحالة يسمح لك لاحقًا بإدخال حالات مرئية جديدة دون كسر الحالات الموجودة. كما يسمح لك بتغيير ما يجب عرضه في كل حالة دون تغيير منطق التفاعل نفسه.
ملخص
- البرمجة التصريحية تعني وصف واجهة المستخدم لكل حالة مرئية بدلاً من التحكم الدقيق في واجهة المستخدم (إلزامي).
- عند تطوير مكون:
Try out some challenges
Challenge 1 of 3:Add and remove a CSS class #
Make it so that clicking on the picture removes the background--active CSS class from the outer <div>, but adds the picture--active class to the <img>. Clicking the background again should restore the original CSS classes.
Visually, you should expect that clicking on the picture removes the purple background and highlights the picture border. Clicking outside the picture highlights the background, but removes the picture border highlight.
