كيفية تبسيط الحالة في تطبيق React - استرجع مع لمسة

تصوير: ترومان أدريان لوباتو دي فاريا

بناء جملة جديد وأسهل بكثير ودلالات لـ Redux القديمة الجيدة

نادراً ما تظهر الكلمتان "بسيط" و "مسترجع" معًا في نفس الجملة. ومع ذلك ، فإن الكثير من مجتمع React قد اعتنق Redux كواحد من أفضل الحلول لتطبيق حالة التطبيق.

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

في هذه المقالة سوف نغطي هذه المواضيع:

  • إدارة تغييرات حالة التطبيق البسيطة
  • العمل مع عمليات المزامنة (مثل جلب البيانات)
  • رمز التطبيق وحالة التطبيق كسول محملة

مكتبة المفاعل

كتبت في الأصل مكتبة مفاعل لتقليل المرجل اللازمة في مشاريعي الشخصية التي تستخدم React. إحدى ميزاته هي إدارة حالة التطبيق الفائقة البسيطة التي سأشاركها معك هنا.

قررت منذ ذلك الحين إتاحة المكتبة لأي شخص قد يبحث عن تبسيط كود React / Redux. استخدمه بحرية؛ انها لك بقدر ما لي.

لتثبيت:

npm تثبيت @ reactorlib / الأساسية

3 أشياء رئيسية

لكتابة إدارة حالة التطبيق الخاصة بنا باستخدام مكتبة مفاعل ، هناك 3 أشياء أساسية نحتاج إلى معرفتها:

  • المتجر: هذا هو المكان الوحيد الذي يتم فيه الاحتفاظ بحالة تطبيقنا بالكامل.
  • الكيانات: هذه أجزاء من حالة التطبيق ، يمثل كل منها مجالًا معينًا من مجالات الاهتمام أو الوظيفة.
  • الإجراءات: هذه هي الوظائف التي يمكن لمكوناتنا التذرع بها لإحداث بعض التغيير في حالة التطبيق. هذه يقيمون أيضا في المتجر.

الخطوة 1: إنشاء الكيانات

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

توفر Reactor Library وظيفة تسمى createEntity التي سنستخدمها لتحديد كياناتنا. يقبل حجتين ، ردود أفعال الكيان ، وكذلك حالته الأولية:

createEntity (ردود الفعل: Object ، initialState: any)

دعنا نخرج الجزء الأسهل من الطريق أولاً. يجب أن تحدد الأوليState أساسًا بنية بيانات كياننا من خلال تعيين قيمة افتراضية لها.

وسيطة التفاعلات عبارة عن تعيين لأسماء الإجراءات مقابل ردود الفعل المقابلة. لاحظ أن التعيين لا يهدف إلى تحديد وظائف الإجراء الفعلي.

في أبسط أشكاله ، يبدو رد الفعل كما يلي:

الإجراء: (الحالة ، الحمولة النافعة) => newState

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

فيما يلي مثال بسيط لتعريف الكيان:

const initialState = {value: 0}؛
عداد const = createEntity (
  {
    الزيادة: (الحالة ، بواسطة) => (
      {... الحالة ، القيمة: state.value + by}
    )،
    إعادة الضبط: state => ({... الولاية ، القيمة: 0})
  }،
  الحالة الأولية
)؛

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

سهل peasy حتى الآن ، أليس كذلك؟ دعنا نذهب على…

الخطوة 2: إعداد المتجر

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

تتضمن مكتبة المفاعلات withStore HOC التي تنشئ المتجر وتضع الكيانات فيه وتعيين المكون المستهدف لها كموفر / مالك للمتجر.

withStore (الكيانات: كائن) (مكون)

هنا وسيطة الكيانات هي تعيين أسماء الكيانات مقابل كائنات الكيان الفعلية التي تم إنشاؤها باستخدام createEntity (). هذا التعيين مهم لأننا نصل إلى الكيانات من المتجر باستخدام الأسماء المحددة هنا.

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

استيراد عداد من './store/counter' ؛
const _App = () => (
  <المسار>
    <شل />
  
const App = withStore ({counter}) (_ App)؛

بهذه البساطة ، حقا. متجرنا هو الآن كل مجموعة.

الخطوة 3: استيراد الدعائم من المتجر

الآن الخطوة الأخيرة المتبقية هي جعل حالة التطبيق في متناول مكوناتنا. هناك 2 قواعد بسيطة:

  • تكون المكونات قادرة على قراءة حالة التطبيق عن طريق استيراد كيانات من المتجر.
  • يمكنهم أيضًا تغيير حالة التطبيق ، من خلال استيراد الإجراءات من المتجر.

نحن نستخدم getPropsFromStore HOC في Reactor Library للقيام بأي منهما أو كليهما ، وحقنهم في مكوننا كدعائم.

getPropsFromStore (
  الكيانات ؟: صفيف <سلسلة> ،
  الإجراءات؟: صفيف <سلسلة>
) (مكون)

الكيانات هنا هي قائمة بأسماء الكيانات ، والإجراءات هي قائمة بأسماء الإجراءات.

يتم حقن الكيانات المستوردة باعتبارها الدعائم الدولة. هذا يعني أنه كلما تغير أي من هذه الكيانات ، سيتم إعادة عرض المكون.

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

قد تتساءل ، أين نحدد وظائف الإجراء هذه؟ حسنا ، نحن لسنا كذلك. يقوم المتجر بإنشاء هذه الأسماء لنا ، استنادًا إلى جميع أسماء الإجراءات التي قمنا بتعيينها لردود الفعل عند إنشاء كياناتنا باستخدام createEntity.

استمرارًا لأمثلة السابقة ، نستورد الكيان المقابل من المتجر على النحو التالي:

const _ClickCount = ({عداد ، زيادة ، إعادة تعيين}) => (
  <>
    لقد قمت بالنقر فوق {counter.value} مرات.
    
    
  
)؛
const ClickCount = getPropsFromStore (
  ['عداد']،
  ["الزيادة" ، "إعادة التعيين"]
() _ ClickCount)؛

هذا هو! في 3 خطوات سهلة ، قمنا بتوصيل المكون الخاص بنا بحالة التطبيق.

العمل مع Async Actions

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

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

تحديد ردود الفعل غير المتزامن

تمكننا CreatEntity من Reactor Library من تحديد ردود الفعل غير المتزامنة بسهولة ، بشكل تعريفي ، في النموذج التالي:

عمل: [
  (الحالة ، الحمولة النافعة) => newState ،
  غير متزامن (الحمولة ، التالي) => {
    نتيجة const = تنتظر doSomethingAsync () ؛
    المقبل (النتيجة)؛
  }،
  (الحالة ، النتيجة) => newState
]

هذا صفيف يتكون من 3 خطوات من رد فعل المتزامن لدينا:

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

يوضح هذا الرسم البياني كيفية تدفق البيانات خلال الخطوات الثلاث لرد الفعل غير المتزامن:

الخطوة الأولى (بدء التشغيل) اختيارية بالفعل ، حيث توجد أوقات لا تحتاج فيها حقًا إلى تغيير الحالة التحضيرية.

مثال للاستخدام

فيما يلي مثال لكيان كامل له ردود فعل بسيطة وغير متزامنة. يمكنك دائمًا الرجوع إلى الرسم التوضيحي أعلاه إذا كان تدفق البيانات وتغييرات الحالة لا يزال غير واضح إلى حد ما.

const initialState = {auth: null، انتظار: false}؛
جلسة const = createEntity (
  {
    تسجيل الدخول: [
      الحالة => ({... الولاية ، الانتظار: صواب}) ،
      غير متزامن ({اسم المستخدم ، كلمة المرور} ، التالي) => {
        استجابة const = في انتظار تسجيل الدخول (اسم المستخدم وكلمة المرور) ؛
        بجانب (الاستجابة).
      }،
      (الحالة ، {auth}) => ({... الولاية ، المصادقة ، الانتظار: خطأ}) ،
    ]،
    logout: state => ({... state ، auth: null}) ،
  }،
  الحالة الأولية
)؛

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

هذا هو! ليست بهذه الطريقة سهلة جدا؟

كسول تحميل الدولة التطبيق

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

نظرًا لأنه لا يمكن أن يكون هناك سوى متجر واحد في التطبيق ، توفر مكتبة Reactor طريقة بسيطة لدمج متاجر الميزات المحملة بالكسل في المتجر الرئيسي. هذا يستخدم withFeatureStore HOC ، الذي يحتوي على التوقيع التالي:

withFeatureStore (الكيانات: كائن) (مكون)

كما قد تلاحظ ، يكون لهذا التنسيق بالضبط نفس تنسيق withStore HOC الذي ناقشناه سابقًا. يحدد الكيانات التي يتم تحميلها كسول مع الوحدات النمطية الخاصة بك الميزة ، للسماح "مكتبة مفاعل" معرفة أن هذه الكيانات سيتم دمجها ديناميكيًا في المتجر بمجرد تحميل الوحدات النمطية الميزة.

مثال للاستخدام

دعنا نأخذ ، على سبيل المثال ، ميزة المؤقت المملوءة بالكسل والتي تحتوي على مكون TimerPage كنقطة دخول ، وكيان مؤقت لإدارة حالته.

استيراد الموقت من './store/timer' ؛
const _TimerPage = () => (
  <العد التنازلي />
)؛
const TimerPage = withFeatureStore ({timer}) (_ TimerPage) ؛

هذا هو! مرة أخرى ، سريعة وسهلة.

مزيد من المعلومات

لمعرفة المزيد حول مكتبة المفاعلات التي استخدمناها في هذه المقالة ، يمكنك العثور على الوثائق الرسمية الخاصة بها على الموقع https://github.com/arnelenero/reactorlib.

شكرا للقراءة.