أفضل 5 ممارسات لاستخدام خطافات تفاعل في الإنتاج

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



باختصار حول نتائج تنفيذ الخطاطيف


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

قدرات تنفيذ هوك


إليك ما قدمته لنا مقدمة الخطافات:

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

نريد الآن مشاركة توصياتنا حول استخدام الخطاطيف في الإنتاج.

1. استرجاع الخطافات في الوقت المناسب من المكونات وإنشاء خطافات مخصصة


من السهل جدًا بدء استخدام الخطافات في المكونات الوظيفية. على سبيل المثال ، تقدمنا ​​بسرعة في مكان ما React.useState، وفي مكان ما - React.useEffectوواصلنا القيام بعملنا. ومع ذلك ، لا يسمح هذا النهج بالاستفادة الكاملة من جميع الفرص التي يمكن أن توفرها الخطافات. التقديم React.useStateعلى شكل صغير use<StateName>State، وفي React.useEffectالشكل use<EffectName>Effectتمكنا من تحقيق ما يلي:

  1. المكونات أصغر من الخطافات العادية.
  2. يمكن إعادة استخدام الخطافات في مكونات مختلفة.
  3. , React, , . , State<StateName> State. React.useState, .

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

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

//  

function Component() {
    const [selected, setSelected] = React.useState()
    const select = React.useCallback(id => setSelected(/** ... */), [
        selected,
        setSelect
    ])

    return <List selectedIds={selected} onSelect={id => select(id)} />
}

//   

const useSelection = () => {
    const [selected, setSelected] = React.useState()
    const select = React.useCallback(id => setSelected(/** ... */), [
        selected,
        setSelect
    ])

    return [selected, select]
}

function Component() {
    const [selected, select] = useSelection()

    return <List selectedIds={selected} onSelect={id => select(id)} />
}

2. حول فوائد React.useDebugValue


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

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

لمساعدة المطور في تصحيح الأخطاء ، أثناء العمل باستخدام أدوات مطور React ، قد يكون من المفيد معرفة اسم العلامة التي تم تحليلها ( flagName) ومتغير قيمة العلامة ( flagVariation). في هذه الحالة ، يمكن أن يساعدنا استخدام خطاف صغير في React.useDebugValue:

export default function useFeatureToggle(flagName, flagVariation = true) {
    const flags = React.useContext(FlagsContext)
    const isFeatureEnabled = getIsFeatureEnabled(flagName, flagVariation)(flags)

    React.useDebugValue({
        flagName,
        flagVariation,
        isEnabled: isFeatureEnabled
    })

    return isFeatureEnabled
}

الآن ، باستخدام أدوات مطور React ، يمكننا رؤية معلومات حول خيار قيمة العلم ، حول اسم العلم ، وما إذا كانت الميزة التي تهمنا قيد التشغيل أم لا.


العمل مع أدوات مطور React بعد تطبيق خطاف React.useDebugValue

يرجى ملاحظة أنه في الحالات التي تستخدم فيها خطاطيف المستخدم خطافات قياسية مثلReact.useStateأوReact.useRef، فإن هذه الخطافات تسجل بالفعل الحالة المقابلة أو بيانات المرجع. ونتيجة لذلك ، فإن استخدام بنيات العرض هناReact.useDebugValue({ state })ليس مفيدًا بشكل خاص.

3. مزيج وتركيب خطافات


عندما بدأنا في تنفيذ الخطافات في عملنا وبدأنا في استخدام المزيد والمزيد من الخطافات في المكونات ، اتضح بسرعة أن 5-10 خطافات يمكن أن تكون موجودة في نفس المكون. في هذه الحالة ، تم استخدام خطاطيف من أنواع مختلفة. على سبيل المثال ، يمكننا استخدام 2-3 خطاطيف React.useState، ثم خطاف React.useContext( على سبيل المثال ، للحصول على معلومات حول المستخدم النشط) ، وخطاف React.useEffect، وكذلك خطافات من مكتبات أخرى ، مثل جهاز التوجيه التفاعلي أو التفاعل الداخلي .

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

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

function OrderCreate() {
    const {
        orderCreater,
        orderFetcher,
        applicationContext,
        goToNextStep,
        pendingOrder,
        updatePendingChanges,
        revertPendingChanges
    } = useOrderCreate()

    return (/** ...children */)
}

4. مقارنة React.useReducer و React.useState


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

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

const initialState = 0;
const reducer = (state, action) => {
    switch (action) {
        case 'increment': return state + 1;
        case 'decrement': return state - 1;
        case 'reset': return 0;
        default: throw new Error('Unexpected action');
    }
};

يمكن اختبار هذا المخفض بشكل منفصل ثم استخدامه في المكون باستخدام React.useReducer:

function Component() {
    const [count, dispatch] = React.useReducer(reducer, initialState);

    return (
        <div>
            {count}
            <button onClick={() => dispatch('increment')}>+1</button>
            <button onClick={() => dispatch('decrement')}>-1</button>
            <button onClick={() => dispatch('reset')}>reset</button>
        </div>
    );
};

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

const CounterActionTypes = {
    Increment: 'increment',
    Decrement: 'decrement',
    Reset: 'reset',
}

const useCounterReducer = (initialState) => {
    const [count, dispatch] = React.useReducer(reducer, initialState);

    const increment = React.useCallback(() => dispatch(CounterActionTypes.Increment));
    const decrement = React.useCallback(() => dispatch(CounterActionTypes.Decrement));
    const reset = React.useCallback(() => dispatch(CounterActionTypes.Reset));

    return {
        count,
        increment,
        decrement
    }
}

function Component() {
    const {count, increment} = useCounterReducer(0);

    return (
        <div>
            {count}
            <button onClick={increment}>+1</button>
        </div>
    );
};

5. التنفيذ التدريجي للخطافات


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

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

export const injectTracking = (propName = 'tracking') => WrappedComponent => {
    const WithTracking = props => {
        const tracking = useTracking();

        const trackingProp = {
            [propName]: tracking,
        };

        return <WrappedComponent {...props} {...trackingProp} />;
    };

    WithTracking.displayName = wrapDisplayName(WrappedComponent, 'withTracking');

    return WithTracking;
};

export default injectTracking;

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

ملخص


في ما يلي بعض الأمثلة عن كيفية تحسين استخدام خطافات التفاعل في قاعدة البيانات الخاصة بنا. نأمل أن تستفيد الخطافات من مشروعك أيضًا.

القراء الأعزاء! هل تستخدم خطافات تفاعلية؟


All Articles