هل تستبدل السنانير في React Redux؟

منذ ظهور الخطافات في React ، كان هناك العديد من الأسئلة حول ما إذا كان يمكنهم استبدال Redux.

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



من أجل توضيح موقفي من React hooks و Redux ، أود أولاً أن أتحدث عن المواقف التي يتم فيها استخدام Redux عادةً.

ما هو الإعادة؟


Redux هي مكتبة تطبق تخزين حالة التطبيق يمكن التنبؤ به. إنها أيضًا بنية تتكامل بسلاسة مع React.

وهنا نقاط القوة الرئيسية ل Redux:

  • التمثيل الحاسم للدولة (بالاشتراك مع المكونات النقية ، وهذا يجعل من الممكن تكوين عناصر بصرية حتمية).
  • دعم تغييرات حالة المعاملات.
  • عزل إدارة الدولة عن آليات I / O والآثار الجانبية.
  • وجود مصدر واحد للبيانات الموثوقة للدولة.
  • سهولة تنظيم التعاون مع الدولة في مختلف المكونات.
  • أدوات تحليل المعاملات (التسجيل التلقائي لكائنات العمل).
  • تصحيح الأخطاء مع القدرة على تسجيل وتشغيل عملية تنفيذ البرنامج (Time Travel Debugging، TTD).

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

ما هي رد فعل السنانير؟


تتيح خطافات التفاعل ، عند العمل مع المكونات الوظيفية ، استخدام تناظرية لحالة المكونات استنادًا إلى فئات ونظائرها في أساليب دورة حياتها. ظهر السنانير في React 16.8.

من بين نقاط القوة الرئيسية في السنانير ما يلي:

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

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

إذا كنت تستخدم أدوات مثل واجهة تفاعل رد فعل رد الفعل مع دعم خطاف ، أو خطاف رد فعل استخدام رد سيكر ، ستجد أنه لا يوجد سبب يدعو للسؤال عما تختاره - خطافات أو ردكس. يمكنك استخدام كل من الجمع بين هذه التقنيات والجمع بينها.

ما يحل محل السنانير؟


بعد ظهور واجهات برمجة التطبيقات للربط ، توقفت عن استخدام التقنيات التالية:


ما لا يحل محل السنانير؟


ما زلت كثيرًا ما أستخدم التقنيات التالية:

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

متى تستخدم السنانير؟


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

يمكن قول الشيء نفسه عن المكونات التي تحتوي على الميزات التالية:

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

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

 import React, { useState } from 'react'; import t from 'prop-types'; import TextField, { Input } from '@material/react-text-field'; const noop = () => {}; const Holder = ({  itemPrice = 175,  name = '',  email = '',  id = '',  removeHolder = noop,  showRemoveButton = false, }) => {  const [nameInput, setName] = useState(name);  const [emailInput, setEmail] = useState(email); const setter = set => e => {    const { target } = e;    const { value } = target;    set(value);  }; return (    <div className="row">      <div className="holder">        <div className="holder-name">          <TextField label="Name">            <Input value={nameInput} onChange={setter(setName)} required />          </TextField>        </div>        <div className="holder-email">          <TextField label="Email">            <Input              value={emailInput}              onChange={setter(setEmail)}              type="email"              required            />          </TextField>        </div>        {showRemoveButton && (          <button            className="remove-holder"            aria-label="Remove membership"            onClick={e => {              e.preventDefault();              removeHolder(id);            }}          >            ×          </button>        )}      </div>      <div className="line-item-price">${itemPrice}</div>      <style jsx>{cssHere}</style>    </div>  ); }; Holder.propTypes = {  name: t.string,  email: t.string,  itemPrice: t.number,  id: t.string,  removeHolder: t.func,  showRemoveButton: t.bool, }; export default Holder; 

useState استخدام useState هنا للتحكم في حالة استخدام حقول name وإدخال email لفترة وجيزة:

 const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email); 

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

إن استخدام الحالة المحلية لأحد المكونات لحل مثل هذه المشكلات دائمًا ما يكون جيدًا ، ولكن قبل أن يتفاعل خطافات React ، كنت أود في أي حال ، حفظ بيانات المكون في Redux-storage والحصول على الحالة من الخصائص.

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

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

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

متى يجب استخدام Redux؟


سؤال آخر شائع بشأن إدارة الدولة هو: "هل أحتاج إلى وضع كل شيء على الإطلاق في مستودع Redux؟ إذا لم أفعل ذلك ، فهل ينتهك القدرة على تصحيح التطبيقات باستخدام آليات TTD؟ "

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

بمعنى آخر ، يمكنك استخدام Redux بأمان ، ولكن يجب أن يكون هناك سبب لذلك. يمكن تبرير استخدام ميزات Redux في المكونات إذا كانت المكونات تختلف في الميزات التالية:

  • يستخدمون I / O. على سبيل المثال ، يعملون مع شبكة أو مع أجهزة معينة.
  • يقومون بحفظ البيانات أو تحميل البيانات منها.
  • انهم يعملون مع دولتهم بالتزامن مع المكونات التي ليست أحفادهم.
  • يتعاملون مع أي منطق أعمال تتعامل معه الأجزاء الأخرى من التطبيق ، إما - يعالجون البيانات المستخدمة في أجزاء أخرى من التطبيق.

فيما يلي مثال آخر مأخوذ من تطبيق TDDDay :

 import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { compose } from 'ramda'; import page from '../../hocs/page.js'; import Purchase from './purchase-component.js'; import { addHolder, removeHolder, getHolders } from './purchase-reducer.js'; const PurchasePage = () => {  //      // mapStateToProps  mapDispatchToProps  const dispatch = useDispatch();  const holders = useSelector(getHolders); const props = {    //           //    dispatch.    addHolder: compose(      dispatch,      addHolder    ),    removeHolder: compose(      dispatch,      removeHolder    ),    holders,  }; return <Purchase {...props} />; }; // `page` -    ,    //        . export default page(PurchasePage); 

لا يتعامل هذا المستند مع DOM. هذا هو مكون العرض. إنه متصل بـ Redux باستخدام واجهة تفاعل رد فعل رد الفعل مع دعم الخطاف .

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

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

قد تكون واجهة React Suspense API مفيدة في المستقبل عند تخزين البيانات في حالة ما وتحميلها منها. نحتاج إلى انتظار إصداره ومعرفة ما إذا كان يمكن استبدال القوالب لحفظ وتحميل بيانات Redux. يتيح لنا Redux الفصل بوضوح بين الآثار الجانبية وبقية منطق المكون ، بينما لا نحتاج إلى العمل مع خدمات I / O بطريقة خاصة. (السبب في أنني أفضل مكتبة redux-saga على الوسيطة redux-thunk هي عزل التأثير). من أجل التنافس مع Redux في هذا السيناريو ، سوف تحتاج واجهة React API إلى توفير عزل التأثير.

الإعادة هي الهندسة المعمارية


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

غالبًا ما استخدم مخفضات Redux style في الحالات التي أحتاج فيها إلى الحفاظ على حالة المكون المعقدة ، لكن لا أحتاج إلى استخدام مكتبة Redux. بالإضافة إلى ذلك ، أستخدم الإجراءات التي تم إنشاؤها بروح Redux (وحتى أدوات Redux مثل Autodux و redux-saga ) لإرسال إجراءات في تطبيقات Node.js. ومع ذلك ، لا أقوم حتى باستيراد Redux إلى هذه التطبيقات.

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

سيصبح هذا خبرًا سارًا لأولئك الذين يرغبون في استخدام حالة المكونات المحلية ذات السنانير في كثير من الأحيان وليس ربط كل شيء بـ Redux.

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

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

سؤال وجواب


هل الحطام مكسور إذا كان Redux لا يدير جميع بيانات التطبيق؟


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

▍ هل يجب أن تلعب مكتبة Redux دور مصدر واحد للبيانات الموثوقة؟


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

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

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

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

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

I هل أحتاج إلى استخدام وظيفة الاتصال من رد فعل رد الفعل ، أم أنه من الأفضل استخدام الخطافات؟


ذلك يعتمد على الكثير. تخلق وظيفة connect مكوّنًا عالي الترتيب مناسبًا للاستخدام المتكرر ، ويتم تحسين الخطافات للتكامل مع مكون واحد.

هل أحتاج إلى توصيل نفس الخصائص بمكونات مختلفة؟ إذا كان الأمر كذلك ، استخدم connect . خلاف ذلك ، أود أن اختيار السنانير. على سبيل المثال ، تخيل أن لديك مكونًا مسؤولاً عن تخويل أذونات إجراءات المستخدم:

 import { connect } from 'react-redux'; import RequiresPermission from './requires-permission-component'; import { userHasPermission } from '../../features/user-profile/user-profile-reducer'; import curry from 'lodash/fp/curry'; const requiresPermission = curry(  (NotPermittedComponent, { permission }, PermittedComponent) => {    const mapStateToProps = state => ({      NotPermittedComponent,      PermittedComponent,      isPermitted: userHasPermission(state, permission),    });    return connect(mapStateToProps)(RequiresPermission);  }, ); export default requiresPermission; 

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

 import NextError from 'next/error'; import compose from 'lodash/fp/compose'; import React from 'react'; import requiresPermission from '../requires-permission'; import withFeatures from '../with-features'; import withAuth from '../with-auth'; import withEnv from '../with-env'; import withLoader from '../with-loader'; import withLayout from '../with-layout'; export default compose(  withEnv,  withAuth,  withLoader,  withLayout(),  withFeatures,  requiresPermission(() => <NextError statusCode={404} />, {    permission: 'admin',  }), ); 

إليك كيفية استخدامه:

 import compose from 'lodash/fp/compose'; import adminPage from '../HOCs/admin-page'; import AdminIndex from '../features/admin-index/admin-index-component.js'; export default adminPage(AdminIndex); 

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

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

▍ إذا كان المنفرد يعتبر مضادًا للنمط و Redux هو منفرد ، هل هذا يعني أن Redux مضاد للنمط؟


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

النتائج


هل يستبدل Redux خطاطيف React؟ السنانير رائعة ، لكنها لا تحل محل Redux.

نأمل أن تساعدك هذه المواد في اختيار نموذج إدارة الولاية لمشاريع React الخاصة بك.

أعزائي القراء! هل واجهت المواقف التي يمكن أن تستبدل خطافات React بها Redux؟

Source: https://habr.com/ru/post/ar464293/


All Articles