أسلوب التفكير في React
تُعدُّ React الطريقة الأمثل لبناء تطبيقات ويب كبيرة وسريعة في JavaScript، فقد كانت مُلائِمة بشكلٍ كبيرٍ لبناء Facebook و Instagram.
من ميّزات React أنّها تجعلنا نُفكِّر بالتطبيقات بينما نبنيها. سنتحدّث في هذا القسم عن أسلوب التفكير لبناء جدول بيانات منتجات قابل للبحث باستخدام React.
البدء بتصميم الشكل المبدئي
فلنفترض أنّنا نمتلك واجهة برمجة تطبيق JSON API وتصميم للشكل المبدئي للجدول المطلوب والذي يبدو كما يلي:
تُعيد JSON API بعض البيانات التي تبدو كما يلي:
[
{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];
الخطوة الأولى: تجزئة واجهة المستخدم إلى تسلسل هرمي من المكونات
أول شيء يجب فعله هو رسم مربّعات حول كل مُكوِّن (وكل مُكوِّن فرعي) في الشكل المبدئي وتسميتها كلّها. إن كُنتَ تعمل مع مُصمِّم فقد يصنع لك هذا الشكل المبدئي باستخدام برنامج Photoshop ويضع أسماء المكونات ضمن طبقات ملف التصميم.
ولكن كيف تعلم ما هي المُكوِّنات التي يجب أن تكون مستقلّة بذاتها وما هي المُكوِّنات التي يجب تقسيمها إلى مُكوِّنات فرعيّة؟ استخدم نفس التقنية لتقرير ما إذا كان عليك إنشاء دالة جديدة أو كائن جديد. وهي تقنيّة تعتمد على مبدأ المسؤولية الوحيدة أي يجب على المُكوِّن أن يقوم بشيء واحد، وإن انتهى به الأمر لفعل عدّة أشياء فينبغي تفكيكه إلى مُكوِّنات فرعيّة.
بما أنّك تعرض عادةً نموذج بيانات بصيغة JSON للمستخدم، فستجد أنّه في حال كان نموذجك مبنيًّأ بشكلٍ صحيح ستكون واجهة المستخدم مترابطة بشكلٍ جيّد (وكذلك بنية مُكوِّناتك)، وذلك لأنّ واجهة المستخدم ونماذج البيانات (data models) تميل للتمسّك بنفس بنية المعلومات، والذي يجعل من عمليّة فصل واجهة المستخدم إلى مُكوِّنات أمرًا سهلًا وبديهيًّا. فقط جزِّءها إلى مُكوِّنات تُمثِّل بالضبط قطعة واحدة من نموذج البيانات لديك.
سترى هنا أنّنا نمتلك خمسة مُكوِّنات في تطبيقنا البسيط، لقد نسقنا البيانات، وكل مكون يمثل:
FilterableProductTable
(باللون البرتقالي): جدول المنتجات المُرشَّحة. يحتوي على كامل المثال.SearchBar
(باللون الأزرق): شريط البحث. يستقبل كل مُدخلات المستخدم.ProductTable
(باللون الأخضر): جدول المنتجات. يعرض ويُرشِّح مجموعة البيانات بناءً على مُدخلات المستخدم.ProductCategoryRow
(باللون التركوازي): صف تصنيفات المنتجات. يعرض ترويسة لكل تصنيف من المنتجات.ProductRow
(باللون الأحمر): صف المنتجات. يعرض صف لكل منتج.
إن نظرتَ إلى جدول المنتجات ProductTable
فستجد أنّ ترويسة الجدول (والتي تحتوي على الاسم Name
والسعر Price
) غير موضوعة في مُكوِّن مستقل لوحدها. تبقى هذه مسألة تفضيل ففي كل الأحوال قد يبقى جدال حول الموضوع. بالنسبة لهذا المثال أبقينا ترويسة الجدول جزءًا من مُكوِّن جدول المنتجات ProductTable
لأنّها جزء من تصيير مجموعة البيانات والتي هي مسؤوليّة هذا المُكوِّن. ولكن إن طوّرنا هذه الترويسة وأصبحت مُعقّدة (أي أضفنا مثلًا إمكانية الترتيب حسب تفضيل مُعيّن)، فمن الأفضل أن نُنشِئ هنا مُكوِّنًا للترويسة ProductTableHeader
.
الآن وقد تعرّفنا على المُكوّنات في تصميمنا، فلنُرتبها في تسلسل مُعيِّن. وهو أمرٌ سهل، فالمُكوِّنات التي تظهر بداخل مُكوِّنات أخرى في التصميم ينبغي أن تظهر كأبناء في التسلسل الهيكلي:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
الخطوة الثانية: بناء إصدار ساكن في React
انتقل إلى شيفرة الخطوة الثانية على موقع CodePen للمتابعة معنا.
الآن وقد أصبح لديك تسلسل هيكلي لمُكوِّناتك، فقد حان الوقت لتنفيذ تطبيقك. أسهل طريقة هي بناء إصدار يستقبل نموذج بياناتك ويُصيِّر واجهة المستخدم بدون وجود تفاعليّة. من الأفضل فصل التفاعليّة عن الإصدار الساكن (static) لأنّ هذا الأخير يتطلّب الكثير من الكتابة والقليل من التفكير، أمّا التفاعليّة فتتطلّب الكثير من التفكير والقليل من الكتابة. سنرى لماذا ذلك.
لبناء إصدار ساكن من تطبيقك يُصيِّر نموذج بياناتك، فيجب عليك بناء مُكوِّنات تُعيد استخدام مُكوِّنات أخرى وتمرير البيانات باستخدام الخاصيّات props
، والخاصيّات هي الطريقة لتمرير البيانات من المُكوِّنات الآباء إلى الأبناء. إن كُنتَ متآلفًا مع مفهوم الحالة (state) فلا تستخدمها مُطلقًا لبناء الإصدار الساكن فهي تُستخدَم من أجل التفاعليّة ولذلك لن تحتاجها هنا.
بإمكانك البناء من الأعلى للأسفل أو من الأسفل للأعلى، أي باستطاعتك البدء من بناء المُكوِّنات ذات الترتيب الأعلى في التسلسل الهرمي (في حالتنا المُكوِّن FilterableProductTable
) أو المُكوِّنات ذات التسلسل الأدنى (في حالتنا المُكوِّن ProductRow
). من الأسهل في الأمثلة البسيطة أن نبدأ من الأعلى للأسفل، وفي الأمثلة الكبيرة من الأسفل للأعلى مع كتابة اختبارات أثناء البناء.
في نهاية هذه الخطوة ستمتلك مكتبة من المُكوِّنات القابلة لإعادة الاستخدام والتي تُصيِّر نموذج بياناتك. تحتوي هذه المُكوِّنات على التابع render()
بما أنّ هذا إصدار ساكن من تطبيقك. يأخذ المُكوِّن ذو الترتيب الأعلى وهو FilterableProductTable
نموذج بياناتك كخاصيّة prop
له. إن أحدثتَ تغييرًا في نموذج بياناتك واستدعيت التابع ReactDOM.render()
مرّةً أخرى، فستُحدَّث واجهة المستخدم. من السهل رؤية كيفيّة تحديث واجهة المستخدم وأين يجب إحداث تغييرات بسبب عدم حدوث شيء مُعقَّد حتى الآن. تُبقي ميّزة React التي تُدعى تدفّق البيانات أحادي الاتجاه (أو الربط أحادي الاتجاه) كل شيء سريعًا ومُوحَّدًا.
ارجع إلى توثيق React إن احتجت للمساعدة في تنفيذ هذه الخطوة.
فاصلة قصيرة: الفرق بين الخاصيّات props
والحالة state
هنالك نوعان من نماذج البيانات في React، وهي الخاصيّات props
والحالة state
. من الهام فهم الفرق بينها، انتقل إلى هذه الصفحة من توثيق React إن كنت لا تعلم الفرق بينها. أو يمكنك الرجوع إلى قسم الأسئلة الشائعة.
الخطوة الثالثة: التعرف على التمثيل الأدنى (ولكن الكامل) لحالة واجهة المستخدم
لتتأكد من أنّ واجهة المستخدم متفاعلة، يجب أن تكون قادرًا على إطلاق التغيّرات إلى نموذج البيانات لديك. تجعل React من هذا أمرًا سهلًا باستخدام الحالة (state).
لبناء تطبيقك على الوجه الصحيح، تحتاج في البداية إلى التفكير بالحد الأدنى من الحالة القابلة للتعديل التي يحتاجها تطبيقك. المفتاح الذهبي هنا هو قاعدة DRY (اختصارًا للجملة Don’t Repeat Yourself أي لا تُكرِّر نفسك). تعرَّف على التمثيل الأدنى من الحالة التي يحتاجها تطبيقك واعتبر كل شيء آخر قد تحتاجه بحسب الطلب. على سبيل المثال إن كُنتَ تبني قائمة بالأشياء الواجب فعلها فاحتفظ بمصفوفة من العناصر الواجب فعلها، لا تحتفظ بمتغيّر منفصل ضمن الحالة لأجل عددها، فعندما تُريد تصيير عددها بإمكانك ببساطة أن تأخذ طول هذه المصفوفة.
فكّر بكل أجزاء البيانات في مثال تطبيقنا هذا، لدينا ما يلي:
- القائمة الأصليّة للمنتجات.
- نص البحث الذي أدخله المستخدم.
- قيمة مربّع التأشير.
- القائمة المُرشَّحة للمنتجات.
فلننظر إلى كل واحدة ونكتشف من هي التي يجب أن تكون ضمن الحالة. اسأل ببساطة ثلاثة أسئلة حول كل جزء من البيانات:
- هل هي مُمرَّرة من مُكوِّن أب عن طريق الخاصيّات
props
؟ إن كانت كذلك فهي ليست حالة غالبًا. - هل تبقى بدون تغيير مع مرور الوقت؟ إن كانت كذلك فهي ليست حالة غالبًا.
- هل بإمكانك حسابها اعتمادًا على أي حالة أو خاصيّات أخرى في المُكوِّن؟ إن كانت كذلك فهي ليست حالة.
تُمرَّر القائمة الأصليّة للمنتجات كخاصيّات props
، لذلك هي ليست حالة. يبدو نص البحث ومُربَّع التأشير بأنّهما حالة لأنّهما يتغيّران مع الوقت ولا يُمكِن حسابهما من أي شيء آخر. أمّا القائمة المُرشَّحة للمنتجات ليست حالة لأنّه يُمكِن حسابها عن طريق جمع القائمة الأصليّة للمنتجات مع نص البحث وقيمة مُربَّع التأشير.
إذًا أخيرًا حالتنا هي:
- نص البحث الذي أدخله المستخدم.
- قيمة مُربَّع التأشير.
الخطوة الرابعة: معرفة أين ينبغي أن تكون الحالة
انتقل إلى شيفرة الخطوة الرابعة على موقع CodePen للمتابعة معنا.
إذًا فقد عرفنا ما هو الحد الأدنى من حالة التطبيق. الآن نحتاج إلى معرفة المُكوِّن الذي يجب أن يُعدِّل ويملك هذه الحالة.
تذكّر مفهوم React حول تدفّق البيانات باتجاه واحد للأسفل في التسلسل الهيكلي للمُكوِّنات. قد لا يبدو واضحًا أي مُكوِّن هو الذي يجب أن يملك الحالة، وهذا هو عادةً أكبر تحدّي للمُطوّرين الجدد في React، لذا اتبع هذه الخطوات لتعرف ذلك:
بالنسبة لكل جزء من الحالة في تطبيقك:
- اعرف كل مُكوِّن يُصيِّر أي شيء اعتمادًا على حالته.
- ابحث عن مُكوِّن مُشترَك (مُكوِّن وحيد فوق كل المُكوِّنات التي تحتاج الحالة في التسلسل الهرمي).
- ينبغي أن يمتلك الحالة إمّا المُكوِّن المشترك أو مُكوِّن آخر ذو ترتيب أعلى في التسلسل الهرمي.
- إن لم تستطع إيجاد المُكوِّن الأكثر منطقيّة لامتلاك الحالة، فأنشِئ مُكوِّنًا جديدًا ببساطة ليحتوي على الحالة وأضفه ضمن التسلسل الهرمي في مكان أعلى من المُكوِّن المشترك.
فلنُطبِّق هذه الاستراتيجيّة على تطبيقنا:
- يحتاج المُكوِّن
ProductTable
إلى ترشيح قائمة المنتجات بناءً على الحالة، ويحتاج المُكوِّنSearchBar
إلى عرض نص البحث وحالة مُربَّع التأشير. - المُكوِّن المشترك هو
FilterableProductTable
. - من المنطقي لنص الترشيح وقيمة مُربَّع التأشير أن يتواجدا ضمن المُكوِّن
FilterableProductTable
.
إذًا فقد قرّرنا أنّ حالتنا ستتواجد في المُكوِّن FilterableProductTable
. أضف في البداية نسخة من الخاصيّة this.state = {filterText: '', inStockOnly: false}
إلى الدالة البانية للمُكوِّن FilterableProductTable
لتعكس الحالة المبدئيّة لتطبيقك. بعدها مرِّر نص الترشيح filterText
و حالة مربّع التأشير وهي inStockOnly
إلى جدول المنتجات ProductTable
وشريط البحث SearchBar
كخاصيّة. استخدم أخيرًا هذه الخاصيّات لتشريح الصفوف في المُكوِّن ProductTable
وتعيين القيم لحقول الإدخال في شريط البحث SearchBar
.
بإمكانك الآن رؤية سلوك تطبيقك: عيِّن نص الترشيح filterText
إلى القيمة "ball"
وحدِّث تطبيقك، سترى كيف يُحدَّث جدول البيانات بشكلٍ صحيح.
الخطوة الخامسة: إضافة الاتجاه العكسي لتدفّق البيانات
انتقل إلى شيفرة الخطوة الخامسة على موقع CodePen للمتابعة معنا.
بنينا حتى الآن تطبيقًا يُصيِّر بشكلٍ صحيح الخاصيّات والحالة والتي تتجه من الأعلى للأسفل في التسلسل الهرمي. حان الوقت الآن لدعم تدفّق البيانات بالاتجاه المعاكس، أي يجب على المُكوِّنات ذات التسلسل الهرمي الأدنى أن تُحدِّث حالة المُكوِّن FilterableProductTable
.
تجعل React من تدفّق البيانات أمرًا يُسهِّل عليك فهم كيفيّة عمل برنامجك، ولكنّه يتطلّب كتابة شيفرة أكثر من ربط البيانات ثنائي الاتجاه الاعتيادي.
إن حاولت الكتابة أو النقر على مربّع التأشير في الإصدار الحالي لمثالنا، فستجد أنّ React تتجاهل مُدخلاتك، وهذا أمرٌ مُتعمَّد لأنّنا عيّنا خاصيّة القيمة value
لحقل الإدخال input
بأن تكون مساويةً للحالة state
المُمرَّرة من المُكوِّن FilterableProductTable
.
فلنُفكِّر بما نريد حدوثه هنا، نريد أن نحرص أنّه حالما يُغيِّر المستخدم حقل الإدخال، أن تتغيّر الحالة لتعكس مُدخلات المستخدم. وبما أنّه يجب على المُكوِّنات تحديث حالتها فقط، فسيُمرِّر المُكوِّن FilterableProductTable
نداءً إلى شريط البحث SearchBar
والذي سيُطلَق عندما يتوجَّب تحديث الحالة. بإمكاننا استخدام الحدث onChange
على حقول الإدخال. تستدعي ردود النداء المُمرَّرة من قبل FilterableProductTable
التابع setState()
ويُحدَّث بذلك التطبيق.
قد يبدو هذا مُعقدًّا، ولكنّه عبارة عن أسطر قليلة من الشيفرة ويوضِّح كيفية انتقال البيانات عبر تطبيقك.
إذًا هذه هي الخطوات
نأمل أن يُعطيك هذا الشرح لمحةً عن أسلوب التفكير لبناء المُكوِّنات والتطبيقات في React. ربّما كتبنا شيفرة أكثر من المُعتاد، ولكن تذكّر أنّنا فعلنا هذا لسهولة قراءة هذه الشيفرة المُقسَّمة إلى وحدات، وهو شيء ستُقدِّره عندما تبني مكتبات كبيرة من المُكوِّنات. وبإعادة استخدام الشيفرة، ستجد أنّ عدد الأسطر المستخدم يقل.
انظر أيضًا
- مثال أهلًا بالعالم في React
- مقدمة إلى JSX
- تصيير العناصر
- المكونات والخاصيات
- حالة ودورة حياة المكونات
- معالجة الأحداث في React
- التصيير الشرطي
- القوائم والمفاتيح
- الحقول
- رفع الحالات المشتركة للمستوى الأعلى
- الفرق بين التركيب والوراثة في React