مقدمة إلى React ، الجزء 2

نشرت: 2020-07-16

مرحبًا بك مجددًا في مقدمتنا إلى React ، حيث نعلمك الأساسيات حتى تفهم كيفية عمل هذه التقنية ، وبالتالي تكتب كودًا أفضل وتنشئ واجهات مستخدم أفضل. في المنشور السابق ، تحدثنا عن كيفية إنشاء مكونات React كوظائف نقية بسيطة. كان الشعار الذي كررته مرارًا وتكرارًا هو أن "مكون React هو وظيفة بسيطة تحصل على مجموعة من الخصائص ( props ) وتولد بعض HTML".

في المرة الأخيرة التي تحدثنا فيها عن React ، انتهينا بالسؤال التالي: إذا كان المكون وظيفة بسيطة ، فكيف يمكننا فعل أي شيء مفيد به؟ حسنًا ، سنتحدث اليوم عن الجزء الديناميكي من React: أي كيف يمكننا جعل المكون يتفاعل مع أحداث المستخدم .

تهيئة البيئة

يحتوي الجزء الأول من هذا البرنامج التعليمي على بعض النظريات ولكن لم يتضمن أي أمثلة ، وأنا أعتذر عن ذلك ؛ أعتقد أن أفضل طريقة للتعلم هي من خلال العمل ، لذلك دعونا نصلح هذا! من الآن فصاعدًا ، سيعتمد كل شيء سأعلمك إياه على مثال سنعمل عليه ، لذا فلنقم بإعداد البيئة التي سنستخدمها أولاً.

سأفترض أن لديك موقع WordPress يمكنك العمل عليه ، بالإضافة إلى node أدوات التطوير و npm . إذا لم يكن الأمر كذلك ، فقد شرحت في هذا المنشور كيف يمكنك استخدام Lando لإنشاء تثبيت WordPress محلي ، وتحتوي وثائق npm على كل ما تحتاج إلى معرفته لتثبيت node و npm .

أولاً ، لنقم بإنشاء ملحق WordPress بسيط حيث سيكون لدينا مكونات React الخاصة بنا. للقيام بذلك ، قم بإنشاء مجلد يسمى react-example في wp-content/plugins ووضع ملف react-example.php داخله بالمحتوى التالي:

 <?php /** * Plugin Name: React Example * Description: Example. * Version: 1.0.0 */ namespace React_Example; defined( 'ABSPATH' ) or die(); function add_page() { add_menu_page( 'React Example', 'React Example', 'read', 'react-example', function () { echo '<div></div>'; } ); } add_action( 'admin_menu', __NAMESPACE__ . '\add_page' ); function enqueue_scripts() { $screen = get_current_screen(); if ( 'toplevel_page_react-example' !== $screen->id ) { return; } $dir = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); if ( ! file_exists( "{$dir}/build/index.asset.php" ) ) { return; } $asset = include "{$dir}/build/index.asset.php"; wp_enqueue_script( 'react-example', "{$url}/build/index.js", $asset['dependencies'], $asset['version'], true ); } add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts' );

يضيف المكون الإضافي ببساطة صفحة جديدة على لوحة معلومات WordPress تسمى React Example ويقوم بتحميل ملف JavaScript فيه. في الوقت الحالي ، يجب أن تكون النتيجة صفحة فارغة:

تفاعل البرنامج التعليمي - صفحة فارغة
صفحة فارغة مع مثال React الخاص بنا.

والتي لا ينبغي أن تكون مفاجأة ، نظرًا لأننا لم ننفذ أي شيء في JavaScript حتى الآن.

كما رأينا في هذا المنشور السابق حول كيفية إضافة زر إلى Gutenberg ، يجب عليك تشغيل الأوامر التالية في جهازك الطرفي (في مجلد البرنامج المساعد الذي نقوم بإنشائه):

 npm init npm install --save-dev @wordpress/scripts

إذا كنت تريد أن تكون قادرًا على كتابة مكونات React وتحويل JavaScript ، فما عليك سوى اتباع التعليمات التي تظهر على الشاشة. أوه ، ولا تنس تحديث ملف package.json الخاص بك بحيث يتضمن جميع scripts ذات الصلة المطلوبة بواسطة @wordpress/scripts .

أخيرًا ، قم بإنشاء مجلد src مع ملف index.js التالي:

 // Import dependencies import { render } from '@wordpress/element'; // Create component const HelloWorld = () => <div>Hello World</div>; // Render component in DOM const wrapper = document.getElementById( 'react-example-wrapper' ); render( <HelloWorld />, wrapper );

قم بتشغيل npm run build (أو npm run start إذا كنت تريد تحويل الشفرة تلقائيًا في كل مرة تقوم فيها بتغيير ملفات المصدر) ، وقم بتحديث الصفحة ، وها هو كل شيء جاهزًا:

مرحبًا بالعالم في برنامجنا التعليمي React
مرحبا بالعالم! تم تنفيذه باستخدام React.

تكوين مكون وظيفي تفاعلي في React

لنقم الآن بإنشاء مكون يتفاعل مع أحداث المستخدم. المثال الذي أريد أن نطبقه معًا هو هذا:

رد الفعل التعليمي - العداد
المكون الخاص بنا عبارة عن عداد صغير به عدة أزرار لزيادة قيمته أو تقليلها.

عداد بسيط يسمح لك بزيادة أو إنقاص قيمته بواحد باستخدام الزرين + و- على التوالي. أعلم أن الأمر بسيط للغاية ، لكنني آمل أن يساعدك على فهم الفرق بين "نموذج البيانات" و "واجهة المستخدم" ، بالإضافة إلى إرشادك خلال تنفيذ المكونات الديناميكية.

منظمة كود أفضل

لنبدأ بتحسين تنظيم الكود الخاص بنا. مثال اليوم بسيط للغاية لدرجة أنك قد ترغب في تخطي هذه الخطوة ، لكنني أعتقد أنه من المفيد أن تكون منظمًا من البداية.

في src ، أنشئ components مجلد جديدة تحتوي على جميع المكونات التي يستخدمها تطبيقنا. نظرًا لأن مثال اليوم يحتوي على مكون واحد فقط ، العداد ، كل ما نحتاج إليه هو إنشاء ملف واحد ، counter.js :

 export function Counter() => ( <p>Counter</p> );

والتي ، كما ترى ، تصدر ببساطة الوظيفة المسؤولة عن تقديم المكون الخاص بنا. بعد ذلك ، قم بتعديل src/index.js بحيث بدلاً من تقديم نموذج HelloWorld الخاص بنا ، فإنه يستورد ويستخدم مكون Counter الجديد الخاص بنا:

 // Import dependencies import { render } from '@wordpress/element'; import { Counter } from './components/counter'; // Render component in DOM const wrapper = document.getElementById( 'react-example-wrapper' ); render( <Counter />, wrapper );

وهذا كل شيء!

تنفيذ مكون مضاد

هدف اليوم هو تنفيذ مكون يتتبع قيمة العداد ، والذي يمكننا زيادته أو إنقاصه من خلال النقر على اثنين من الأزرار. هذا يعني أن المكون الخاص بنا يجب أن يحتوي على ثلاثة أشياء:

  • عنصر لعرض القيمة الحالية
  • زر + لزيادة القيمة المذكورة
  • أ- زر لتقليله

يعد تنفيذ هذا المكون أمرًا سهلاً للغاية:

 export const Counter = ( { value } ) => ( <div> <div>Counter: <strong>{ value }</strong></div> <button>+</button> <button>-</button> </div> );

بما أنك تعلم أن المكون الخاص بك يأخذ value كأحد دعائمه ويجب أن يتضمن زرين. لسوء الحظ ، قد لا تعرف ما يجب فعله بعد ذلك إذا كنت تريد أن تقوم هذه الأزرار بتعديل القيمة ، خاصة إذا أخذنا في الاعتبار ، في المرة الأخيرة التي تحدثنا فيها عن هذا ، أخبرتك "لا يمكن للمكونات تغيير خصائصها".

الوظائف كدعائم وفصل المسؤولية

تذكر: مكون React هو وظيفة تستقبل الخصائص وتولد HTML. وهذا يعني أنه يجب على أي شخص يستدعي هذه الوظيفة (أو بمعنى آخر أي شخص يستخدم هذا المكون) أن يعطيها جميع المعلمات (الخصائص) التي تحتاجها.

لقد أخبرك حدسك بالفعل أن قيمة العداد هي إحدى الخصائص التي يحتاجها . لا نعرف من أين تأتي هذه القيمة (ولا نهتم) ، لكننا نعلم أن المكون الخاص بنا سيحصل على هذه القيمة. أنت تعلم أيضًا أن مكون العداد الخاص بنا يجب أن يكون قادرًا على تعديل هذه القيمة بطريقة ما . بمعنى آخر ، يجب أن يكون المكون على هذا النحو ، عندما ينقر المستخدم فوق + أو - ، يجب تحديث القيمة.

إذا فكرت للحظة في هذه العبارة ، فسوف تدرك بسرعة أن مكون العداد لا يتوقع فقط value ( رقم النوع) ، ولكنه يحتاج أيضًا إلى خاصيتين إضافيتين لوظيفة النوع: أحدهما يزيد القيمة المذكورة ، والآخر واحد ينقصها. بعبارة أخرى ، يحتاج Counter إلى ثلاث props مختلفة:

  • value : هو رقم وقيمة العداد (duh!)
  • onIncrease : هي وظيفة تزيد من قيمة العداد عند استدعائها
  • onDecrease : هي وظيفة أخرى ، عند استدعائها ، تقلل من قيمة العداد

إذا عملنا على افتراض أن المكون الخاص بنا سيحصل على هذه الخاصيات الثلاثة ، فإن المكون نفسه يصبح بسيطًا للغاية:

 export const Counter = ( { value, onIncrease, onDecrease } ) => ( <div> <div>Counter: <strong>{ value }</strong></div> <button onClick={ onIncrease }>+</button> <button onClick={ onDecrease }>-</button> </div> );

لاحظ كيف أن المكون الخاص بنا لا ينفذ الوظائف التي يتلقاها (والتي ، كما قلنا بالفعل ، "ممنوعة" لأنها قد تؤدي إلى آثار جانبية). كل ما يفعله هو توصيلهم بحدث click لأزرارنا. هذا يعني أننا نجحنا في تنفيذ وظيفة خالصة (كما ينبغي أن تكون جميع المكونات) لأنه ، في ظل نفس الخاصيات الثلاثة ، ستكون النتيجة دائمًا نفس HTML مع نفس "الاتصالات".

لسوء الحظ ، إذا حاولت استخدام المكون ، فلن ترى أي شيء يعمل

إعطاء المكون جميع الدعائم التي يحتاجها

ولا عجب أنه لا يعمل! عندما استخدمنا هذا المكون ، لم نحترم عقده ولم نضف الدعائم التي يتطلبها للعمل بشكل صحيح. انظر فقط إلى index.js :

 render( <Counter />, wrapper );

العداد ليس له value ولا يحتوي على وظائف onIncrease أو onDecrease !

حسنًا ، دعنا نصلح هذا بسرعة. كل ما يتعين علينا القيام به هو إنشاء متغير يخزن قيمة العداد وتنفيذ وظيفة تقوم بتحديثه ، حتى نتمكن من استخدامها في المكون الخاص بنا:

 // Import dependencies import { render } from '@wordpress/element'; import { Counter } from './components/counter'; // Store value let value = 0; function setValue( newValue ) { value = newValue; } // Render component in DOM const wrapper = document.getElementById( 'react-example-wrapper' ); render( <Counter value={ value } onIncrease={ () => setValue( value + 1 ) } onDecrease={ () => setValue( value - 1 ) } />, wrapper );

بفضل هذه البنية ، تمكنا من فصل واجهة المستخدم (وهو المكون) تمامًا عن إدارة البيانات. المكون حيادي تمامًا بشأن من أو كيف يتم تخزين القيمة ؛ كل ما يهمه هو أنه يحصل على الدعائم التي يحتاجها.

إذا قمنا بتحديث الصفحة مرة أخرى ، فسترى أن العداد يعرض الآن قيمة عداد افتراضية (أي 0 ) ، ولكن النقر فوق + و- لا يعمل حتى الآن ...

إعادة تقديم المكون عند التحديث

إذا نظرت إلى المقتطف السابق ، فقد تعتقد أن الأشياء يجب أن تعمل الآن. لكن من الواضح أنهم لا يفعلون ذلك ... فما هو الخطأ؟ حسنًا ، دعنا نجري اختبارًا صغيرًا. قم بتعديل وظيفة setValue بحيث ، بالإضافة إلى تعيين القيمة الجديدة value ، فإنها تسجلها أيضًا في وحدة التحكم:

 function setValue( newValue ) { value = newValue; console.log( 'Value is', value ); }

وتحقق مرة أخرى إذا كانت الأشياء تعمل كما هو متوقع. فقط افتح أدوات مطور متصفحك وانقر على الأزرار الخاصة بك:

رد فعل المثال حيث لا يتم تحديث المكون
لم يتم تحديث العداد في واجهة المستخدم ، ولكن يبدو أن الأشياء تعمل كما هو متوقع في وحدة التحكم ...

ممتع ، أليس كذلك؟ يبدو أنه يتم تحديث value بشكل صحيح في كل مرة ينقر فيها المستخدم على زر ، ولكن المكون لا يتم تحديثه أبدًا.

إذا كنت تريد تصيير مكون ، فأنت بحاجة إلى استدعاء وظيفته الخالصة باستخدام الخاصيات المطلوبة. عندما تتغير خاصية ، فأنت بحاجة إلى استدعاء الدالة الصافية مرة أخرى بالقيمة الجديدة ، بحيث يمكن إنشاء HTML الجديد. لذلك ، إذا أردنا أن تعرض واجهة المستخدم أحدث value ، فنحن بحاجة إلى إعادة عرض المكون يدويًا في كل مرة يتم فيها تحديث value :

 // Import dependencies import { render } from '@wordpress/element'; import { Counter } from './components/counter'; // Store value let value = 0; function setValue( newValue ) { value = newValue; refreshComponent(); } // Render component in DOM const wrapper = document.getElementById( 'react-example-wrapper' ); function refreshComponent() { render( <Counter value={ value } onIncrease={ () => setValue( value + 1 ) } onDecrease={ () => setValue( value - 1 ) } />, wrapper ); } refreshComponent();

كما ترى ، لقد أنشأنا ببساطة وظيفة جديدة تسمى refreshComponent نستخدمها لعرض المكون في المرة الأولى وفي كل مرة يتم تحديث value عبر setValue . ينتج عن هذا السلوك المتوقع:

رد الفعل التعليمي - العداد
المكون يعمل بشكل صحيح.

حقا؟ جعل المكونات تبدو مثل حماقة!

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

لقد تعلمت اليوم درسين مهمين للغاية :

  1. الخصائص التي قد يحتاجها المكون ليست فقط "بيانات" (مثل الأرقام والسلاسل والمصفوفات والكائنات ...) ولكن يمكن أن تكون أيضًا "وظائف". هذا يعني أنه يمكننا ربط هذه الوظائف بأحداث DOM بحيث يتم تشغيلها "تلقائيًا" عندما يقوم المستخدم بتنفيذ إجراءات معينة وبالتالي تحديث حالة تطبيقنا.
  2. المكونات (واجهة المستخدم) والبيانات التي نستخدمها مستقلة تمامًا عن بعضها البعض. لا يهتم مكون React بمن أو كيف تدار الدعائم التي يحتاجها. هذا يعني أنه يمكننا تنفيذ حل سيئ لإدارة وتحديث value العداد الخاص بنا والأشياء "ستعمل فقط".

في المنشور التالي سنقوم بتحسين هذا الحل واستخدام متاجر WordPress (التي تعتمد على Redux) لإدارة حالة التطبيق. لا تفوتها!

صورة مميزة لـ Hermes Rivera على Unsplash.