في جزء اليوم من ترجمة دورة React ، نقترح عليك إكمال المهمة العملية التالية وتقديم انتباهكم إلى قصة حول كيفية تعديل حالة مكونات React.

→
الجزء 1: نظرة عامة على الدورة ، وأسباب شعبية React ، ReactDOM و JSX→
الجزء 2: المكونات الوظيفية→
الجزء 3: ملفات المكونات ، هيكل المشروع→
الجزء 4: مكونات الوالدين والطفل→
الجزء 5: بدء العمل في تطبيق TODO ، أساسيات التصميم→
الجزء 6: حول بعض ميزات الدورة ، JSX وجافا سكريبت→
الجزء 7: الأنماط المضمنة→
الجزء 8: مواصلة العمل على تطبيق TODO ، الإلمام بخصائص المكونات→
الجزء 9: خصائص المكون→
الجزء 10: ورشة عمل حول العمل مع خصائص المكون والتصميم→
الجزء 11: توليد العلامات الديناميكية وطريقة صفائف الخريطة→
الجزء 12: ورشة العمل ، المرحلة الثالثة من العمل على طلب TODO→
الجزء 13: المكونات القائمة على الفصل→
الجزء 14: ورشة عمل حول المكونات القائمة على الفصل ، وحالة المكون→
الجزء 15: ورش العمل الصحية المكونة→
الجزء 16: المرحلة الرابعة من العمل على طلب TODO ، التعامل مع الأحداث→
الجزء 17: المرحلة الخامسة من العمل على تطبيق TODO ، وتعديل حالة المكونات→
الجزء 18: المرحلة السادسة من العمل على طلب TODO→
الجزء 19: طرق دورة حياة المكونالجزء 20: الدرس الأول في التقديم الشرطي→
الجزء 21: الدرس الثاني وورشة العمل حول التقديم الشرطي→
الجزء 22: المرحلة السابعة من العمل على تطبيق TODO ، وتنزيل البيانات من مصادر خارجية→
الجزء 23: الدرس الأول حول العمل مع النماذج→
الجزء 24: نماذج الدرس الثاني→
الجزء 25: ورشة عمل حول العمل مع النماذج→
الجزء 26: بنية التطبيق ، نمط الحاوية / المكون→
الجزء 27: مشروع المقررالدرس 31. ورشة العمل. تطبيق TODO. المرحلة رقم 5
→
الأصلob الوظيفة
عند بدء تشغيل تطبيق Todo الخاص بنا ، قد تلاحظ أن إعلامًا يتم عرضه في وحدة التحكم يشير إلى أننا ، بعد تكوين الخاصية
checked
لعنصر في مكون
TodoItem
، لم نوفر آلية للتفاعل مع هذا العنصر في شكل
onChange
أحداث
onChange
. عند العمل مع واجهة التطبيق ، يؤدي هذا إلى حقيقة أنه لا يمكن التحقق من الأعلام المعروضة على الصفحة أو إلغاء تحديدها.
تتم دعوتك هنا لتجهيز عنصر من نوع
checkbox
لمكون
TodoItem
أحداث
TodoItem
، وهو في هذه المرحلة من العمل ، يكفي لتقديمه في شكل دالة تقوم بإخراج شيء ما إلى وحدة التحكم.
olution الحل
إليك ما يبدو عليه رمز مكون
TodoItem
الآن ، والذي يتم تخزينه في ملف
TodoItem.js
:
import React from "react" function TodoItem(props) { return ( <div className="todo-item"> <input type="checkbox" checked={props.item.completed}/> <p>{props.item.text}</p> </div> ) } export default TodoItem
هذا هو ما تعرضه وحدة التحكم عند بدء تشغيل التطبيق.
إشعار وحدة التحكمفي الوقت نفسه ، لا تستجيب الأعلام لآثارنا.
للتخلص من هذا الإشعار وإعداد المشروع لمزيد من العمل ، يكفي تعيين
onChange
أحداث
onChange
إلى عنصر
checkbox
. إليك ما يبدو في الكود:
import React from "react" function TodoItem(props) { return ( <div className="todo-item"> <input type="checkbox" checked={props.item.completed} onChange={() => console.log("Changed!")} /> <p>{props.item.text}</p> </div> ) } export default TodoItem
نحن هنا ، كمعالج ، نستخدم وظيفة بسيطة تقوم بإخراج الكلمة
Checked!
إلى وحدة التحكم
Checked!
. في الوقت نفسه ، لا يؤدي النقر فوق الأعلام إلى تغيير في حالتها ، ولكن يختفي الإخطار الوارد من وحدة التحكم ، كما يظهر في الشكل التالي.
لا تزال الأعلام لا تعمل ، ولكن اختفى الإشعار من وحدة التحكمسيسمح لنا هذا التغيير البسيط الذي تم إجراؤه على التطبيق ، بعد أن نتعامل مع التغيير في حالة المكونات ، بجعل مربعات الاختيار تعمل بشكل صحيح.
الدرس 32. تغيير حالة المكونات
→
الأصللنبدأ بتطبيق قياسي تم إنشاؤه باستخدام
create-react-app
، في ملف
App.js
الذي يحتوي على الكود التالي:
import React from "react" class App extends React.Component { constructor() { super() this.state = { count: 0 } } render() { return ( <div> <h1>{this.state.count}</h1> <button>Change!</button> </div> ) } } export default App
يحتوي
index.css
أنماط
index.css
، والذي تم
index.js
في ملف
index.js
، على وصف الأنماط التالية:
div { display: flex; flex-direction: column; align-items: center; justify-content: center; } h1 { font-size: 3em; } button { border: 1px solid lightgray; background-color: transparent; padding: 10px; border-radius: 4px; } button:hover { cursor: pointer; } button:focus { outline:0; }
في هذه المرحلة ، يبدو التطبيق كما هو موضح في الشكل التالي.
صفحة التطبيق في المتصفحاليوم سوف نتحدث عن كيفية تغيير حالة المكونات. إذا كان للمكون حالة ، فإن هذا يسمح ، عن طريق تهيئة ذلك ، بتخزين بعض البيانات فيه. لكن إذا تعذر تغيير الحالة ، فلن يكون المكون ذا فائدة خاصة من وجوده ، ولن يكون تخزين البيانات فيه مختلفًا تمامًا ، على سبيل المثال ، عن نسخها في كود المكون.
دعنا نتحدث عن التطبيق ، على سبيل المثال الذي سننظر في العمل مع حالة المكون. مكون
App
الذي تم عرض الكود أعلاه هو مكون قائم على الفصل. هذا واضح تمامًا ، لأننا نحتاج إلى أن يكون لهذا المكون دولة. في رمز المكون ، نستخدم المنشئ.
في ذلك ، نحن ، كما هو الحال دائمًا ، نسمي الأسلوب
super()
ونقوم بتهيئة الحالة عن طريق كتابة خاصية
count
إليها وتعيين قيمة أولية لها بقيمة
0
. في طريقة
render()
، نقوم بطباعة رأس من المستوى الأول يمثل قيمة خاصية
count
من حالة المكون ، بالإضافة إلى زر به كلمة
Change!
. كل هذا مهيأ باستخدام الأنماط.
في هذه المرحلة من العمل على التطبيق ، افتحه في متصفح وانقر على الزر ، بالطبع ، لن يحدث شيء. لكننا بحاجة إلى النقر فوق الزر لتغيير حالة المكون ، مما يؤثر على خاصية
count
. في الوقت نفسه ، قمنا بالفعل بدراسة منهجية معالجة الأحداث في React ، ومهمتنا هي إنشاء آلية تقوم ، من خلال الاستجابة للنقرة على زر ، بتغيير خاصية حالة
count
.
دعنا ننتقل إلى حل مهمتنا من خلال تزويد الزر
onClick
الأحداث
onClick
، والذي ، بالنسبة للمبتدئين ، سينتج ببساطة شيء ما إلى وحدة التحكم.
للقيام بذلك ، سنضيف طريقة جديدة لفئة المكون. يمكنك أن تسميها أي شيء تريده ، ولكن من المعتاد استدعاء هذه الأساليب بحيث تشير أسمائها إلى الأحداث التي تعالجها. نتيجة لذلك ، نظرًا لأننا
handleClick()
لمعالجة حدث
click
،
handleClick()
. إليك ما سيكون عليه رمز مكون
App
الآن.
import React from "react" class App extends React.Component { constructor() { super() this.state = { count: 0 } } handleClick() { console.log("I'm working!") } render() { return ( <div> <h1>{this.state.count}</h1> <button onClick={this.handleClick}>Change!</button> </div> ) } } export default App
يرجى ملاحظة أنه بالإشارة إلى هذه الطريقة من
render()
، فإننا نستخدم إنشاء النموذج
this.handleClick
.
الآن ، إذا قمت بالنقر فوق الزر ، ستظهر الرسالة المقابلة في وحدة التحكم.
النقر فوق الزر يستدعي طريقة الفصل.الآن دعونا نجعل ذلك حتى يؤدي النقر فوق الزر إلى زيادة الرقم المعروض أعلاه ، أي تعديل حالة المكون. ربما حاول تغيير حالة المكون مباشرة ، في أسلوب
handleClick()
؟ دعنا نقول أننا إذا أعدنا كتابة هذه الطريقة مثل هذا:
handleClick() { this.state.count++ }
يجب أن أقول على الفور أن هذا لا يعمل مع حالة المكونات في React. محاولة تنفيذ مثل هذا الرمز سوف يلقي خطأ.
يمكن مقارنة حالة المكون بالملابس التي يرتديها الشخص. إذا كان يريد تغيير الملابس ، فهو لا يغير أو يعيد طلاء الملابس دون أن يخلع نفسه ، لكنه يخلعها ويضع شيئًا آخر. في الواقع ، هذا هو بالضبط كيفية عملها مع حالة المكونات.
قد تتذكر أننا كنا نتحدث عن طريقة خاصة تُستخدم لتعديل الحالة ، وهي متوفرة في المكونات استنادًا إلى الفئات نظرًا لأنها تمدد فئة
React.Component
. هذه هي طريقة
setState()
. يتم استخدامه في الحالات التي تحتاج فيها إلى تغيير حالة المكون. هذه الطريقة يمكن استخدامها بطرق مختلفة.
أذكر أن الدولة هي كائن. دعنا نحاول تمرير طريقة
setState()
إلى كائن سيحل محل الحالة. نعيد كتابة أسلوب
handleClick()
يلي:
handleClick() { this.setState({ count: 1 }) }
سيؤدي محاولة استخدام هذه الطريقة إلى حدوث الخطأ التالي:
TypeError: Cannot read property 'setState' of undefined
. في الواقع ، ما نتحدث عنه الآن يسبب الكثير من الجدل بين مطوري React ، والآن سأريك طريقة بسيطة للغاية لحل هذه المشكلة ، والتي قد تبدو غير عادية للوهلة الأولى.
النقطة
handleClick()
هي أنه في كل مرة ، وإنشاء طريقة فئة (
handleClick()
في حالتنا) ، والتي من المقرر أن تستخدم طريقة
setState()
، يجب أن ترتبط هذه الطريقة مع
this
. ويتم ذلك في المنشئ. رمز المكون بعد هذا التعديل سيبدو كما يلي:
import React from "react" class App extends React.Component { constructor() { super() this.state = { count: 0 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ count: 1 }) } render() { return ( <div> <h1>{this.state.count}</h1> <button onClick={this.handleClick}>Change!</button> </div> ) } } export default App
الآن بعد النقر على زر
Change!
سيظهر الرقم 1 فوقه ، لن يتم عرض رسائل الخطأ.
الضغط على زر يعدل الحالةصحيح ، تحول الزر إلى "لمرة واحدة". بعد النقرة الأولى عليه ، يتغير
0
إلى
1
، وإذا نقرت عليه مرة أخرى ، فلن يحدث شيء. بشكل عام ، هذا ليس مستغربا. يؤدي الرمز الذي يتم استدعاؤه عند النقر على الزر ، وظيفته ، في كل مرة يتم فيها تغيير الحالة إلى حالة جديدة ، ومع ذلك ، بعد النقرة الأولى على الزر ، لن تختلف الحالة الجديدة ، حيث يتم تخزين الرقم
1
في خاصية
count
، عن الحالة القديمة. لحل هذه المشكلة ، خذ بعين الاعتبار طريقة أخرى للعمل مع الأسلوب
setState()
.
إذا لم نكن مهتمين بالحالة السابقة للمكون ، فيمكننا ببساطة تمرير كائن إلى هذه الطريقة ، والتي ستحل محل الحالة. لكن غالبًا ما يحدث أن الحالة الجديدة للمكون تعتمد على القديم. في حالتنا ، يعني هذا ، استنادًا إلى قيمة خاصية
count
، التي يتم تخزينها في الإصدار السابق من الحالة ، أننا نريد إضافة 1 إلى هذه القيمة. في الحالات التي تحتاج فيها إلى تغيير الحالة التي تحتاج إلى
setState()
بما تم تخزينها مسبقًا فيه ، يمكنك تمرير طريقة
setState()
كدالة ، كمعلمة ، تتلقى الإصدار السابق من الحالة. يمكنك تسمية هذه المعلمة كما تريد ، وفي حالتنا ستكون
prevState
. سيبدو شراء هذه الوظيفة كما يلي:
handleClick() { this.setState(prevState => { }) }
قد تعتقد أنه في مثل هذه الوظيفة يكفي أن تشير ببساطة إلى الحالة باستخدام صيغة لهذا النموذج.
this.state
، ولكن هذا النهج لن
this.state
. لذلك ، من المهم أن تقبل هذه الوظيفة الإصدار السابق من حالة المكون.
يجب أن ترجع الدالة إصدارًا جديدًا من الحالة. إليك كيف
handleClick()
طريقة
handleClick()
هذه المشكلة:
handleClick() { this.setState(prevState => { return { count: prevState.count + 1 } }) }
لاحظ أنه للحصول على القيمة الجديدة لخاصية
count
، نستخدم
count: prevState.count + 1
. قد تعتقد أن إنشاء
count: prevState.count++
النماذج
count: prevState.count++
، لكن عامل التشغيل
++
count: prevState.count++
المتغير الذي يتم تطبيقه عليه ، فهذا يعني محاولة لتعديل الإصدار السابق من الحالة ، لذلك نحن لا نستخدمه هنا.
سيبدو الرمز الكامل لملف المكون في هذه المرحلة كما يلي:
import React from "react" class App extends React.Component { constructor() { super() this.state = { count: 0 } this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState(prevState => { return { count: prevState.count + 1 } }) } render() { return ( <div> <h1>{this.state.count}</h1> <button onClick={this.handleClick}>Change!</button> </div> ) } } export default App
الآن كل نقرة على زر يزيد من قيمة العداد.
كل نقرة على الزر تزيد من قيمة العداد.ما اكتشفناه للتو يفتح فرصًا كبيرة لنا في تطوير تطبيقات React.
قلنا سابقًا أن المكون الأصل يمكنه ، من خلال آلية خاصية ، نقل الخصائص من حالته الخاصة إلى المكونات الفرعية. إذا اكتشف React تغييراً في حالة المكون الأصل ، فسوف يعيد تقديم المكون الفرعي الذي تم تمرير هذه الحالة إليه. يبدو مثل استدعاء الأسلوب
render()
. نتيجة لذلك ، سيعكس المكون الفرعي البيانات الجديدة المخزنة في حالة المكون الأصل.
ملخص
لقد أعددت اليوم تطبيق Todo لمزيد من العمل عليه ، كما تعرفت على الآليات المستخدمة في React لتغيير حالة المكون. في المرة القادمة سيُطلب منك توسيع قدرات التطبيق التدريبي باستخدام ما تعلمته اليوم.
أعزائي القراء! كيف تشعر حيال حقيقة أن حالة المكونات في React لا يمكن تغييرها مباشرة دون استخدام آليات خاصة؟
