كيفية تقليل العدد وزيادة قابلية قراءة التعليمات البرمجية في ملف red-redux، redux-saga

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

ما لا يناسبني


إن مكتبات رد الفعل المتفاعل و redux-saga بسيطة ومرنة ومريحة ، ولكن لها تكرار للكود. العناصر الرئيسية هي:

  1. مصانع الحدث


    const actionCreatorFactory = type => payload => ({ type, payload });
    
    export const INITIALIZE = 'INITIALIZE';
    
    export const ON_FIELD_CHANGE = 'ON_FIELD_CHANGE';
    export const onFieldChange = actionCreatorFactory(ON_FIELD_CHANGE);
    
    export const HANDLE_FIELD = 'HANDLE_FIELD';
    export const handleField = actionCreatorFactory(HANDLE_FIELD);
    
    export const GO_TO_NEXT_STEP = 'GO_TO_NEXT_STEP';
    export const goToNextStep = actionCreatorFactory(GO_TO_NEXT_STEP);
    

    على هذا النحو ، هناك العديد من الأشياء التي تربكني:

    - وصف لأنواع الأحداث. في هذا المثال ، يمكنك بالتأكيد الاستغناء عن الثابت ، ولكن لا يزال عليك تمرير نوعه في المصنع ، والذي سيكون مطابقًا لاسم الأحداث التي تم إنشاؤها في إعلام الجمل ( camelCase ).

    - إذا نسيت بنية الحمولة ، لتذكرها ، تحتاج إلى الانتقال إلى المخفض / الملحمة ، حيث يتم استخدام هذا الحدث ، ومعرفة ما يجب نقله هناك
  2. المخفضات


    import getInitialState, {
      formDataInitialState as initialState,
    } from '../helpers/initialState';
    import { HANDLE_FIELD_DONE, ON_FIELD_CHANGE, RESET } from '../actionCreators';
    
    export default (state = initialState, { type, payload }) => {
      switch (type) {
        case RESET: {
          return getInitialState().formDataInitialState;
        }
    
        case ON_FIELD_CHANGE: {
          const { name } = payload;
          return {
            ...state,
            [name]: '',
          }
        }
    
        case HANDLE_FIELD_DONE: {
          const { name, value } = payload;
          return {
            ...state,
            [name]: value,
          }
        }
      }
    
      return state;
    };
    

    هنا ، بشكل عام ، فقط استخدام بناء التبديل مزعج
  3. ساجاس


    import { all, put, select, fork, takeEvery } from 'redux-saga/effects';
    import { runServerSideValidation } from '../actionCreators';
    import { HANDLE_FIELD } from '../actionCreators';
    
    function* takeHandleFieldAction() {
      yield takeEvery(HANDLE_FIELD, function*({ payload }) {
        const { validation, formData } = yield select(
          ({ validation, formData }) => ({
            validation: validation[payload.name],
            formData,
          })
        );
    
        const valueFromState = formData[payload.name];
    
        if (payload.value !== valueFromState) {
          const { name, value } = payload;
          const { validator } = validation.serverValidator;
          yield put(
            runServerSideValidation({
              name,
              value,
              validator,
              formData,
            })
          );
        }
      });
    }
    
    export default function* rootSaga() {
      yield all([fork(takeHandleFieldAction())]);
    }
    

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

    import { all, put, select, fork, takeEvery } from 'redux-saga/effects';
    import { runServerSideValidation } from '../actionCreators';
    import { HANDLE_FIELD } from '../actionCreators';
    
    function* takeHandleFieldWorker({ payload }) {
        const { validation, formData } = yield select(
          ({ validation, formData }) => ({
            validation: validation[payload.name],
            formData,
          })
        );
    
        const valueFromState = formData[payload.name];
    
        if (payload.value !== valueFromState) {
          const { name, value } = payload;
          const { validator } = validation.serverValidator;
          yield put(
            runServerSideValidation({
              name,
              value,
              validator,
              formData,
            })
          );
      }
    }
    
    function* takeHandleFieldWatcher() {
      yield takeEvery(HANDLE_FIELD, takeHandleFieldWorker);
    }
    
    export default function* rootSaga() {
      yield all([fork(takeHandleFieldWatcher())]);
    }
    

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

كيف أحاول حل هذه المشاكل


دعنا نذهب بالترتيب.

  1. مصانع الحدث


    import { actionsCreator, payload } from 'sweet-redux-saga';
    
    @actionsCreator()
    class ActionsFactory {
      initialize;
      
      @payload('field', 'value')
      onFieldChange;
      
      @payload('field')
      handleField;
      
      @payload('nextStep')
      goToNextStep;
    }
    

    actionsCreator(). , (initialize;/onFieldChange;/handleField;/gpToNextStep;) action creator. , payload(...[fieldNames]). :

    class ActionsFactory {
      initialize = () => ({
        type: 'INITIALIZE',
        payload: undefined,
      });
    
      onFieldChange = (field, value) => ({
        type: 'ON_FIELD_CHANGE',
        payload: {
          field,
          value,
        },
      });
    
      handleField = field => ({
        type: 'HANDLE_FIELD',
        payload: {
          field,
        },
      });
    
      goToNextStep = nextStep => ({
        type: 'GO_TO_NEXT_STEP',
        payload: {
          nextStep,
        },
      });
    }
    

    toString, toPrimitive, valueOf. :

    const actionsFactory = new ActionsFactory();
    console.log(String(actionsFactory.onFieldChange)); // 'ON_FIELD_CHANGE'
    

  2. import getInitialState, {
      formDataInitialState as initialState,
    } from '../helpers/initialState';
    import { HANDLE_FIELD_DONE, ON_FIELD_CHANGE, RESET } from '../actionCreators';
    import { reducer } from '../../../leadforms-gen-v2/src/decorators/ReducerDecorator';
    
    @reducer(initialState)
    export class FormDataReducer {
      [RESET]() {
        return getInitialState().formDataInitialState;
      }
    
      [ON_FIELD_CHANGE](state, payload) {
        const { name } = payload;
        return {
          ...state,
          [name]: '',
        };
      }
    
      [HANDLE_FIELD_DONE](state, payload) {
        const { name, value } = payload;
        return {
          ...state,
          [name]: value,
        };
      }
    }
    

    reducer([initialState]). , , .

    function reducer(state = initialState, action) {
      if (!action) {
        return state;
      }
    
      const reducer = instance[action.type];
      if (reducer && typeof reducer === 'function') {
        return reducer(state, action.payload);
      }
    
      return state;
    }
    

  3. import { all, put, select, } from 'redux-saga/effects';
    import { runServerSideValidation } from '../actionCreators';
    import { HANDLE_FIELD } from '../actionCreators';
    import { sagas, takeEvery, filterActions } from 'sweet-redux-saga';
    
    @sagas()
    class MySagas {
      @takeEvery([HANDLE_FIELD])
      @filterActions(
        ({state, payload }) => state.formData[payload.name] === payload.value
      )
      * takeHandleFieldAction({ payload }) {
        const { validation, formData } = yield select(({ validation, formData }) => ({
          validation: validation[payload.name],
          formData,
        }));
    
        const { name, value } = payload;
        const { validator } = validation.serverValidator;
        yield put(
          runServerSideValidation({
            name,
            value,
            validator,
            formData,
          })
        );
      }
    }
    
    export default function* rootSaga() {
      yield all([
        new MySagas(),
      ]);
    }
    

    إنشاء فصل دراسي مع تعليق توضيحي sagas (). عند إنشاء مثيل للفصل ، نحصل على منشئ وظائف يستدعي جميع حقول الفصل المميزة بعلامة TakeEvery ([... [actionTypes]]) أو TakeLatest ([... [actionTypes]] التعليقات التوضيحية في سلسلة محادثات منفصلة:

    function* mySagas() {
      yield all([fork(mySagas.takeHandleFieldAction())]);
    }
    

    يمكن أيضًا استخدام التعليق التوضيحي لـ filterActions ({state، type، payload}) مع الحقول ، وفي هذه الحالة سيتم استدعاء الملحمة فقط إذا أعادت الدالة true .

استنتاج


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

لقد صنعت هذه التعليقات التوضيحية في حزمة sweet-redux-saga . إذا كانت هناك حلول أخرى ، فسوف يسعدني مشاركتها معي.

All Articles