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

نشرت: 2020-07-30

رأينا الأسبوع الماضي أن مكون React ليس أكثر من تمثيل رسومي (جزء من) حالة تطبيقنا. في المثال الذي قمنا بتطبيقه ، أظهر أحد المكونات قيمة العداد مع زرين يسمحان بزيادة أو تقليل القيمة المذكورة. كانت الحيلة بسيطة: يمكن أن تكون props المكون بيانات ووظائف .

حتى الآن ، فإن الفكرتين الرئيسيتين اللتين تعلمناهما من الجزء الأول والجزء الثاني من هذا البرنامج التعليمي هما:

  • مكوّن React هو وظيفة خالصة تستقبل مجموعة من الخصائص وتُنشئ HTML المطلوب لتصييرها.
  • يمكن أن تكون الخصائص التي يتلقاها مكون React هي (أ) البيانات التي يجب أن تظهر في واجهة المستخدم و (ب) الوظائف التي بمجرد توصيلها كردود استدعاء لأحداث DOM ، تعدل حالة تطبيقنا.

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

تمديد مثالنا

في الجزء الثاني من هذا البرنامج التعليمي ، أنشأنا عدادًا بزرين لزيادة قيمته أو تقليلها. للقيام بذلك ، أضفنا متغيرًا متغيرًا في ملف index.js مع وظيفة لتغييره. مكون العداد لدينا:

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

يمكن بعد ذلك تلقي جميع الدعائم التي تحتاجها:

 // 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 );

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

عدادات متعددة مع React و Redux
عدادات متعددة منفذة بمكونات React ومتجر WordPress قائم على Redux.

تحديد حالة تطبيقنا

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

  • إضافة عداد جديد
  • حذف العداد س
  • اضبط قيمة العداد x
  • احصل على قائمة بجميع العدادات الموجودة في تطبيقنا
  • احصل على قيمة العداد س

لكنك قد ترغب في استخدام نهج مختلف (على سبيل المثال ، قد ترغب في إنشاء وظيفة لزيادة عداد معين ووظيفة مختلفة لتقليله).

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

اعتمادًا على ماهية هذا x ، قد ترغب في استخدام بنية بيانات واحدة أو أخرى. إذا كان x ، على سبيل المثال ، هو فهرس العداد (أي أنك تريد أن تكون قادرًا على استرداد قيمة العداد الأول أو الثاني أو الثالث ) ، فقد تكون مجموعة من الأرقام كافية. إذا كنت تريد أن يكون x معرّف عداد فريدًا ، فقد ترغب في استخدام طريقة مختلفة. على سبيل المثال ، قد ترغب في استخدام قاموس مع أزواج المعرف / القيمة:

 const counters = { ae13a: 0, f18bb: 3, e889a: 1, 8b1d3: -5, };

أو مجموعة من العناصر:

 const counters = [ { id: 'ae13a', value: 0 }, { id: 'f18bb', value: 3 }, { id: 'e889a', value: 1 }, { id: '8b1d3', value: -5 }, ];

إذا قمت أولاً بتعريف الواجهة (المحددات والمعرفات ، إن شئت) للتعامل مع حالة التطبيق والوصول إليها ، فعندئذٍ تكون كيفية تنفيذ الحالة نفسها عبارة عن صندوق أسود. هذا يعني أنه يمكنك تغيير المتجر نفسه في أي وقت وستعمل الأشياء كما هو متوقع طالما أنك تحتفظ بواجهة برمجة التطبيقات.

إنشاء متجر Redux باستخدام WordPress

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

  1. اسم يعرّف متجرك بشكل فريد
  2. كائن selectors يحتوي على جميع المحصلات لاسترداد البيانات من المخزن
  3. كائن actions له وظيفة تؤدي إلى طلبات التحديث (سنتحدث عما يعنيه هذا لاحقًا في هذا المنشور)
  4. وظيفة reducer مسؤولة عن تحديث الحالة عند تشغيل إجراءات معينة

دعونا نرى مثالا حقيقيا ، أليس كذلك؟ استمرارًا لمثال الأسبوع الماضي ، قم بإنشاء مجلد store جديد في src . ثم أنشئ ملف index.js فيه بالشفرة التالية:

 // Load dependencies import { registerStore } from '@wordpress/data'; import reducer from './reducer'; import * as actions from './actions'; import * as selectors from './selectors'; registerStore( 'react-example/counters', { actions, reducer, selectors, } );

المقتطف واضح تمامًا ، أليس كذلك؟ نحن ببساطة نستورد وظيفة registerStore التي كنا نتحدث عنها وبعض التبعيات التي لم ننشئها بعد ( reducer ، actions ، selectors ) ، ونقوم بتسجيل المتجر الجديد. لاحظ كيف قمنا بتسمية متجرنا للتأكد من أنه فريد: لقد جمعنا اسم المكون الإضافي الخاص بنا ( react-example ) مع كلمة تحدد موضوع المتجر ( counters ). سهل جدا!

الإجراءات في المتجر

من الأشياء التي يحتاجها كل متجر (من حيث المبدأ) هي مجموعة من الوظائف التي تسمح لنا بتعديل حالته. في مثالنا ، ستكون الإجراءات في src/store/actions.js :

 export function addCounter( counterId ) { return { type: 'ADD_COUNTER', counterId, }; } export function removeCounter( counterId ) { return { type: 'REMOVE_COUNTER', counterId, }; } export function setCounterValue( counterId, value ) { return { type: 'SET_COUNTER_VALUE', counterId, value, }; }

كما هو متوقع ، متجرنا لديه ثلاثة إجراءات لتحديث حالته:

  • addCounter : وظيفة تضيف عدادات جديدة في متجرنا. الحجة الوحيدة التي تتطلبها هي معرف العداد الجديد.
  • removeCounter : وظيفة تزيل عدادًا موجودًا. مرة أخرى ، الحجة الوحيدة التي تستخدمها هي معرف العداد الذي نريد إزالته.
  • setCounterValue : دالة تحدد قيمة جديدة لعداد معين. من الواضح أن هذه الوظيفة تأخذ وسيطتين: معرف العداد للتحديث وقيمته الجديدة.

الآن ، إذا نظرت عن كثب في كل إجراء ، فقد تفاجأ: لا يبدو أن أيًا من هذه الإجراءات يُحدِّث أي شيء. بدلا من ذلك ، يعيدون الأشياء. ماذا يجري هنا؟

في Redux (وتعتمد متاجر WordPress على Redux) ، لا تقوم الإجراءات بتعديل المتجر مباشرةً. بدلاً من ذلك ، يقومون بإنشاء كائن يشير إلى "طلب تحديث". تتبع هذه الطلبات دائمًا نفس النمط: فهي كائن بسمة type تحدد الطلب بشكل فريد والعديد من الخصائص الإضافية اللازمة لتطبيق التحديث المطلوب بنجاح.

لنرى الآن كيف يمكن فعلاً تحديث الحالة ...

تنفيذ المخفض لتحديث حالة المتجر

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

المخفض هو وظيفة تأخذ الحالة الحالية لتطبيقنا وإجراء أرسله شخص ما ويقوم بتحديث الحالة من خلال تطبيق التحديث المطلوب .

في القسم السابق ، رأينا أن متجرنا لديه ثلاثة إجراءات ، لذلك يجب أن يكون مخفضنا قادرًا على تطبيق كل منها:

 import { omit } from 'lodash'; export default function reducer( state = {}, action ) { switch ( action.type ) { case 'ADD_COUNTER': return { ...state, [ action.counterId ]: 0, }; case 'REMOVE_COUNTER': return omit( state, action.counterId ); case 'SET_COUNTER_VALUE': return { ...state, [ action.counterId ]: action.value, }; } return state; }

كما ترى ، يتلقى المخفض state السابقة (والتي ، بالمناسبة ، افتراضيًا هي الكائن الفارغ {} ) والإجراء المرسل مع المعلومات لتحديث الحالة. جسم المخفض بسيط للغاية:

  • يبدأ ببيان switch لتمييز نوع التحديث (نوع action.type ) الذي يجب تشغيله:
    • إذا كان ADD_COUNTER ، فإنه يقوم بإنشاء كائن state جديد مع إجراء مفتاح جديد action.counterId مع ضبط قيمة العداد على 0 .
    • إذا كان REMOVE_COUNTER ، فإنه يقوم بإنشاء كائن state جديد بدون action.counterId الرئيسي.
    • إذا كان SET_COUNTER_VALUE ، فإنه يقوم بإنشاء كائن state جديد حيث يتم الآن تعيين القيمة قيد action.counterId على action.value .

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

المحددات في المتجر

الآن بعد أن عرفت كيفية تحديث حالة متجرك ، كل ما عليك تعلمه هو كيفية الاستعلام عنه. ما عليك سوى تحديد selectors.js باستخدام وظائف الاستعلام التي تحتاجها:

 export function getCounterIds( state ) { return Object.keys( state ); } export function getCounterValue( state, counterId ) { return state[ counterId ]; }

وهذا كل شيء! واضح جدا ، أليس كذلك؟ محددات المتجر هي وظائف تأخذ (على الأقل) وسيطة واحدة ( state المتجر) وتعيد قيمة محددة . من الواضح ، يمكن أن تحتوي المحددات على مزيد من الحجج إذا كنت بحاجة إلى إرجاع قيمة معينة من داخل متجرك.

في حالتنا ، على سبيل المثال ، أنشأنا محددين:

  • تعيد getCounterIds مجموعة من معرفات العداد. نظرًا لأننا قمنا بتنفيذ المتجر باستخدام قاموس / كائن ، فنحن مهتمون ببساطة بإعادة Object.keys الخاصة به.
  • تُرجع getCounterValue القيمة المحددة لعداد معين. تأخذ هذه الوظيفة وسيطين ( state الحالية لتطبيقنا ومعرف العداد الذي نهتم به) وتعيد القيمة المطلوبة.

كيفية اختبار متجرنا

لاختبار عمل المتجر بشكل صحيح ، افتح ملف src/index.js import

 // Import dependencies import { render } from '@wordpress/element'; import './store'; import { Counter } from './components/counter'; ...

بعد ذلك ، قم بتحويل الشفرة (باستخدام npm run build ) وانتقل إلى متصفحك. افتح أدوات المطور واستخدم وحدة تحكم JavaScript لكتابة بعض الأوامر:

 dispatch = wp.data.dispatch( 'react-example/counters' ); select = wp.data.select( 'react-example/counters' ); dispatch.addCounter( 'a' ); dispatch.addCounter( 'b' ); dispatch.addCounter( 'c' ); dispatch.setCounterValue( 'a', 3 ); select.getCounterIds(); // Array(3) [ "a", "b", "c" ] select.getCounterValue( 'a' ); // 3

باستخدام dispatch WordPress select الوظائف ، ستتمكن من رؤية أن المتجر يعمل كما هو متوقع. ونصيحة إضافية: هناك امتداد لكل من Firefox و Chrome يسمى Redux DevTools يسمح لك بمشاهدة متاجر Redux الخاصة بك بشكل صحيح:

Redux DevTools في Firefox
يسمح لك امتداد Redux DevTools لمتصفح Firefox و Chrome بالتحقق بسهولة من حالة متاجر Redux.

الخطوات التالية

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

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

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

 const counters = [ { id: 'ae13a', value: 0 }, { id: 'f18bb', value: 3 }, { id: 'e889a', value: 1 }, { id: '8b1d3', value: -5 }, ];

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

صورة مميزة بواسطة Annie Theby على Unsplash.