في هذا الجزء من ترجمة البرنامج التعليمي 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: مشروع المقررالدرس 44. هندسة التطبيق ، نمط الحاوية / المكون
→
الأصلفي بعض الأحيان يكون حجم العمل الذي يكون المكون المنفصل مسؤولاً عنه كبيرًا جدًا ؛ ويتعين على المكون حل الكثير من المهام. يتيح لك استخدام نمط الحاوية / المكون إمكانية فصل منطق التطبيق عن منطق تكوين التمثيل المرئي. يتيح لك ذلك تحسين بنية التطبيق ، ومشاركة المسؤولية عن أداء المهام المختلفة بين المكونات المختلفة.
في الدرس العملي السابق ، أنشأنا مكونًا ضخمًا يصل طول رمزه إلى 150 سطرًا. إليك الكود الذي حصلنا عليه بعد ذلك:
import React, {Component} from "react" class App extends Component { constructor() { super() this.state = { firstName: "", lastName: "", age: "", gender: "", destination: "", isVegan: false, isKosher: false, isLactoseFree: false } this.handleChange = this.handleChange.bind(this) } handleChange(event) { const {name, value, type, checked} = event.target type === "checkbox" ? this.setState({ [name]: checked }) : this.setState({ [name]: value }) } render() { return ( <main> <form> <input name="firstName" value={this.state.firstName} onChange={this.handleChange} placeholder="First Name" /> <br /> <input name="lastName" value={this.state.lastName} onChange={this.handleChange} placeholder="Last Name" /> <br /> <input name="age" value={this.state.age} onChange={this.handleChange} placeholder="Age" /> <br /> <label> <input type="radio" name="gender" value="male" checked={this.state.gender === "male"} onChange={this.handleChange} /> Male </label> <br /> <label> <input type="radio" name="gender" value="female" checked={this.state.gender === "female"} onChange={this.handleChange} /> Female </label> <br /> <select value={this.state.destination} name="destination" onChange={this.handleChange} > <option value="">-- Please Choose a destination --</option> <option value="germany">Germany</option> <option value="norway">Norway</option> <option value="north pole">North Pole</option> <option value="south pole">South Pole</option> </select> <br /> <label> <input type="checkbox" name="isVegan" onChange={this.handleChange} checked={this.state.isVegan} /> Vegan? </label> <br /> <label> <input type="checkbox" name="isKosher" onChange={this.handleChange} checked={this.state.isKosher} /> Kosher? </label> <br /> <label> <input type="checkbox" name="isLactoseFree" onChange={this.handleChange} checked={this.state.isLactoseFree} /> Lactose Free? </label> <br /> <button>Submit</button> </form> <hr /> <h2><font color="#3AC1EF">Entered information:</font></h2> <p>Your name: {this.state.firstName} {this.state.lastName}</p> <p>Your age: {this.state.age}</p> <p>Your gender: {this.state.gender}</p> <p>Your destination: {this.state.destination}</p> <p>Your dietary restrictions:</p> <p>Vegan: {this.state.isVegan ? "Yes" : "No"}</p> <p>Kosher: {this.state.isKosher ? "Yes" : "No"}</p> <p>Lactose Free: {this.state.isLactoseFree ? "Yes" : "No"}</p> </main> ) } } export default App
العيب الأول لهذا الكود ، الذي يلفت انتباهك على الفور ، هو أنه عند العمل بها ، عليك دائمًا التمرير في نافذة المحرر.
يمكنك ملاحظة أن الجزء الأكبر من هذا الرمز هو منطق تشكيل واجهة التطبيق ، ومحتويات طريقة التقديم
render()
. بالإضافة إلى ذلك ، هناك قدر معين من التعليمات البرمجية مسؤول عن تهيئة حالة المكون. يحتوي المكون أيضًا على ما يسمى "منطق العمل" (أي ، ما ينفذ منطق أداء التطبيق). هذا هو رمز الأسلوب
handleChange()
.
وفقًا لنتائج بعض الدراسات ، من المعروف أن قدرة المبرمج على إدراك الرمز الذي يبحث عنه ضعيفة جدًا إذا كانت الشفرة طويلة بما فيه الكفاية ، ويتعين على المبرمج استخدام التمرير لعرضه بالكامل. لقد لاحظت هذا خلال الفصول الدراسية. عندما يتضح أن الكود الذي أتحدث عنه طويل جدًا ، ويتعين عليَّ التمرير فيه باستمرار ، يصبح من الصعب على الطلاب إدراكه.
سيكون من الجيد لو قمنا بإعادة صياغة التعليمات البرمجية الخاصة بنا ، حيث نشارك المسؤولية بين المكونات المختلفة لتشكيل واجهة التطبيق (ما هو موصوف الآن في طريقة التقديم
render()
) ولتنفيذ منطق التطبيق ، أي من خلال تعريف كيف ينبغي أن يبدو الواجهة (يتم تمثيل الكود المقابل الآن بواسطة مُنشئ المكون الذي تتم تهيئة الحالة فيه ،
handleChange()
أحداث التحكم
handleChange()
). عند استخدام هذا النهج في تصميم التطبيق ، فإننا ، في الواقع ، نعمل مع نوعين من المكونات ، وتجدر الإشارة إلى أنك قد تواجه أسماء مختلفة لمثل هذه المكونات.
سوف نستخدم نمط الحاوية / المكون هنا. عند استخدامه ، يتم تصميم التطبيقات من خلال تقسيم المكونات إلى نوعين - مكونات المكون (تشير الكلمة حاوية في اسم النموذج إليهم) ومكونات العرض التقديمي (هذا هو المكون باسم النمط). في بعض الأحيان تسمى مكونات الحاوية مكونات "ذكية" ، أو ببساطة "حاويات" ، وتسمى مكونات العرض التقديمي مكونات "غبية" ، أو ببساطة "مكونات". هناك أسماء أخرى لهذه الأنواع من المكونات ، وتجدر الإشارة إلى أن المعنى المضمن في هذه الأسماء قد يختلف ، من حالة إلى أخرى ، في ميزات معينة. بشكل عام ، تتمثل الفكرة العامة لهذا النهج في أن لدينا مكون حاوية يكون مسؤولاً عن تخزين الحالة ويحتوي على طرق لإدارة الحالة ، ويتم نقل منطق تشكيل الواجهة إلى مكون آخر - مكون العرض التقديمي. هذا المكون مسؤول فقط عن استلام الخصائص من مكون الحاوية وعن التكوين الصحيح للواجهة.
→
هنا مادة دان أبراموف التي يستكشف فيها هذه الفكرة.
نقوم بتحويل رمز طلبنا وفقًا لنموذج الحاوية / المكون.
أولاً ، دعنا ننتبه إلى حقيقة أنه يتم الآن تجميع كل شيء في التطبيق في مكون واحد من
App
. تم تصميم هذا التطبيق بطريقة لتبسيط هيكله قدر الإمكان ، ولكن في المشروعات الحقيقية ، يصعب على مكون
App
نقل مهمة تقديم النموذج وإدراج كود فيه بغرض تنظيم عمل الآليات الداخلية لهذا النموذج.
أضف إلى نفس المجلد الذي يوجد به الملف
App.js
، والملف
Form.js
، حيث سيتم وضع رمز المكون الجديد. ننقل كل الشفرة من مكون
App
إلى هذا الملف ،
App
مكون
App
، الذي يمثله الآن المكون القائم على الفصل ، إلى مكون وظيفي ، تتمثل مهمته الرئيسية في إخراج مكون
Form
. لا تنسى استيراد مكون
Form
إلى مكون
App
. نتيجة لذلك ، سيبدو رمز مكون
App
كما يلي:
import React, {Component} from "react" import Form from "./Form" function App() { return ( <Form /> ) } export default App
إليك ما يعرضه التطبيق على الشاشة في هذه المرحلة من العمل.
تطبيق في المتصفحفي الفصول السابقة ، أخبرتك أنني أفضل أن يكون مكون
App
بمثابة "جدول محتويات" للتطبيق ، والذي يشير إلى أي ترتيب يتم عرض أقسامه على الصفحة ، ممثلة بمكونات أخرى مفوضة بمهام تتمثل في عرض أجزاء كبيرة من التطبيق.
لقد قمنا بتحسين بنية التطبيق بشكل طفيف ، لكن المشكلة الرئيسية ، المعبر عنها في حقيقة أن أحد المكونات يتحمل مسؤولية كبيرة ، لم يتم حلها بعد. لقد نقلنا ببساطة كل ما كان في وقت سابق في مكون
App
إلى مكون
Form
. لذلك ، نحن الآن بصدد حل هذه المشكلة. للقيام بذلك ، قم بإنشاء ملف آخر -
FormComponent.js
في نفس المجلد الذي
Form.js
App.js
ملفات
App.js
و
App.js
سيمثل هذا الملف مكون العرض التقديمي المسؤول عن رؤية النموذج. في الواقع ، يمكنك تسميتها بشكل مختلف ، يمكنك هيكلة ملفات المكونات بشكل مختلف ، كل هذا يتوقف على احتياجات وحجم مشروع معين.
Form.js
الملف
Form.js
على منطق النموذج ، أي رمز مكون الحاوية. لذلك ، قم بإعادة تسميته إلى
FormContainer.js
وقم بتغيير أمر الاستيراد في رمز مكون
App
، مع إحضاره إلى هذا النموذج:
import Form from "./FormContainer"
يمكنك أيضًا إعادة تسمية مكون
Form
إلى
FormContainer
، لكننا لن نفعل ذلك. سنقوم الآن بنقل الكود المسؤول عن تقديم النموذج من ملف
FormContainer.js
إلى ملف
FormComponent.js
.
المكون
FormComponent
سيكون وظيفي. إليكم كيف سيبدو رمزه في هذه المرحلة من العمل:
function FormComponent(props) { return ( <main> <form> <input name="firstName" value={this.state.firstName} onChange={this.handleChange} placeholder="First Name" /> <br /> <input name="lastName" value={this.state.lastName} onChange={this.handleChange} placeholder="Last Name" /> <br /> <input name="age" value={this.state.age} onChange={this.handleChange} placeholder="Age" /> <br /> <label> <input type="radio" name="gender" value="male" checked={this.state.gender === "male"} onChange={this.handleChange} /> Male </label> <br /> <label> <input type="radio" name="gender" value="female" checked={this.state.gender === "female"} onChange={this.handleChange} /> Female </label> <br /> <select value={this.state.destination} name="destination" onChange={this.handleChange} > <option value="">-- Please Choose a destination --</option> <option value="germany">Germany</option> <option value="norway">Norway</option> <option value="north pole">North Pole</option> <option value="south pole">South Pole</option> </select> <br /> <label> <input type="checkbox" name="isVegan" onChange={this.handleChange} checked={this.state.isVegan} /> Vegan? </label> <br /> <label> <input type="checkbox" name="isKosher" onChange={this.handleChange} checked={this.state.isKosher} /> Kosher? </label> <br /> <label> <input type="checkbox" name="isLactoseFree" onChange={this.handleChange} checked={this.state.isLactoseFree} /> Lactose Free? </label> <br /> <button>Submit</button> </form> <hr /> <h2><font color="#3AC1EF">Entered information:</font></h2> <p>Your name: {this.state.firstName} {this.state.lastName}</p> <p>Your age: {this.state.age}</p> <p>Your gender: {this.state.gender}</p> <p>Your destination: {this.state.destination}</p> <p>Your dietary restrictions:</p> <p>Vegan: {this.state.isVegan ? "Yes" : "No"}</p> <p>Kosher: {this.state.isKosher ? "Yes" : "No"}</p> <p>Lactose Free: {this.state.isLactoseFree ? "Yes" : "No"}</p> </main> ) }
إذا نظرت إلى هذا الرمز ، يصبح من الواضح أننا لا نستطيع أن نقصر أنفسنا على مجرد نقله من ملف إلى آخر ، لأن هناك الآن روابط للحالة (على سبيل المثال ،
this.state.firstName
) ومعالج الأحداث (
this.handleChange
) ، الذي كان في نفس المكون استنادًا إلى الفئة التي كانت بها شفرة العرض هذه. الآن ، سيتم أخذ كل ما تم نقله مسبقًا من نفس الفئة التي يوجد بها رمز العرض من الخصائص التي تم تمريرها إلى المكون. هناك بعض المشاكل الأخرى. سنقوم الآن بإصلاح هذا الرمز ، لكن أولاً سنعود إلى رمز مكون
Form
، والذي يوجد الآن في ملف
FormContainer.js
.
طريقة
render()
فارغة الآن. نحتاج إلى عرض مكون
FormComponent
في هذه الطريقة ونحتاج إلى تنظيم نقل الخصائص الضرورية إليه. نحن نستورد
FormComponent
في ملف
Form
FormComponent
في
Form
FormComponent
render()
،
FormComponent
معالج الأحداث ،
FormComponent
، كحالة. الآن سيبدو رمز مكون
Form
كما يلي:
import React, {Component} from "react" import FormComponent from "./FormComponent" class Form extends Component { constructor() { super() this.state = { firstName: "", lastName: "", age: "", gender: "", destination: "", isVegan: false, isKosher: false, isLactoseFree: false } this.handleChange = this.handleChange.bind(this) } handleChange(event) { const {name, value, type, checked} = event.target type === "checkbox" ? this.setState({ [name]: checked }) : this.setState({ [name]: value }) } render() { return( <FormComponent handleChange={this.handleChange} data={this.state} /> ) } } export default Form
سنقوم
FormComponent
رمز مكون
FormComponent
،
FormComponent
إلى النموذج التالي:
import React from "react" function FormComponent(props) { return ( <main> <form> <input name="firstName" value={props.data.firstName} onChange={props.handleChange} placeholder="First Name" /> <br /> <input name="lastName" value={props.data.lastName} onChange={props.handleChange} placeholder="Last Name" /> <br /> <input name="age" value={props.data.age} onChange={props.handleChange} placeholder="Age" /> <br /> <label> <input type="radio" name="gender" value="male" checked={props.data.gender === "male"} onChange={props.handleChange} /> Male </label> <br /> <label> <input type="radio" name="gender" value="female" checked={props.data.gender === "female"} onChange={props.handleChange} /> Female </label> <br /> <select value={props.data.destination} name="destination" onChange={props.handleChange} > <option value="">-- Please Choose a destination --</option> <option value="germany">Germany</option> <option value="norway">Norway</option> <option value="north pole">North Pole</option> <option value="south pole">South Pole</option> </select> <br /> <label> <input type="checkbox" name="isVegan" onChange={props.handleChange} checked={props.data.isVegan} /> Vegan? </label> <br /> <label> <input type="checkbox" name="isKosher" onChange={props.handleChange} checked={props.data.isKosher} /> Kosher? </label> <br /> <label> <input type="checkbox" name="isLactoseFree" onChange={props.handleChange} checked={props.data.isLactoseFree} /> Lactose Free? </label> <br /> <button>Submit</button> </form> <hr /> <h2><font color="#3AC1EF">Entered information:</font></h2> <p>Your name: {props.data.firstName} {props.data.lastName}</p> <p>Your age: {props.data.age}</p> <p>Your gender: {props.data.gender}</p> <p>Your destination: {props.data.destination}</p> <p>Your dietary restrictions:</p> <p>Vegan: {props.data.isVegan ? "Yes" : "No"}</p> <p>Kosher: {props.data.isKosher ? "Yes" : "No"}</p> <p>Lactose Free: {props.data.isLactoseFree ? "Yes" : "No"}</p> </main> ) } export default FormComponent
تم إصلاح الرمز هنا مع الأخذ في الاعتبار حقيقة أن المكون يتلقى الآن البيانات ورابطًا إلى معالج الأحداث عبر الخصائص.
بعد كل هذه التحولات ، لن يتغير مظهر النموذج ولا الطريقة التي يعمل بها ، لكننا قمنا بتحسين بنية كود المشروع ، على الرغم من أن حجم رمز مكون
FormComponent
لا يزال كبيرًا. ومع ذلك ، الآن هذا الكود يحل مشكلة واحدة فقط ، هو المسؤول الوحيد عن التصور للنموذج. لذلك ، أصبح العمل معه الآن أسهل بكثير.
نتيجة لذلك ، حققنا فصل المسؤوليات بين المكونات. مكون
Form
من ملف
FormContainer.js
الآن مشغول بشكل خاص بواسطة منطق التطبيق ، بينما
FormComponent
مكون
FormComponent
من ملف
FormComponent.js
فقط على التعليمات البرمجية التي تشكل واجهة التطبيق. مكون
App
الآن مسؤول فقط عن تجميع الصفحة من كتل كبيرة.
تجدر الإشارة إلى أنه نظرًا لوجود مكتبات مثل
Redux
و
Context
API التي تم إصدارها مؤخرًا ، لم يعد نمط الحاوية / المكون الذي تمت مناقشته هنا مناسبًا كما كان من قبل. على سبيل المثال ، يمكن لـ Redux دعم الحالة العامة للتطبيق الذي يمكن أن تستخدمه المكونات.
النتائج
في هذا الدرس ، قمنا بفحص استخدام نمط الحاوية / المكون ، والذي يهدف إلى تقسيم المكونات إلى تلك المسؤولة عن تكوين واجهة التطبيق وتلك المسؤولة عن تخزين الحالة وعن منطق التطبيق. يساعد تطبيق هذا النمط على تحسين بنية الكود الخاص بتطبيقات React ويسهل عملية التطوير. في المرة القادمة سوف نعمل على مشروع الدورة.
أعزائي القراء! ما أنماط التصميم التي تستخدمها عند تطوير تطبيقات React؟
