Redux, как повторно использовать редуктор / действия? - PullRequest
0 голосов
/ 30 сентября 2018

Я пытаюсь создать HOC или хотя бы уменьшить количество редукторов / действий.Поэтому я создаю одну библиотеку-редуктор, которая будет хранить все мои данные, и одно действие getItemList для обработки каждого действия.При вызове действия из реагировать componentDidMount () я передам такой параметр, как (продукт, пользователь и т. Д.), Этот параметр будет знать, какой API и какое состояние обновлять (например: state.library.product).

Я хотел бы получить ваш совет по поводу этой техники, это хороший способ?

Спасибо

const initialState = {
    contact: {
        tmp_state: { addresses: {} },
        item: null,
        receivedAt: null,
        isFetching: false,
        isError: false,
        list: []
    },
    expense: {
        tmp_state: {},
        item: null,
        receivedAt: null,
        isFetching: false,
        isError: false,
        list: []
    },
    service: {
        tmp_state: {},
        item: null,
        receivedAt: null,
        isFetching: false,
        isError: false,
        list: []
    },

    product: {
        tmp_state: {},
        item: null,
        receivedAt: null,
        isFetching: false,
        isError: false,
        list: []
    }
};

export default (state = initialState, action) => {

    // Init reducer name
    var name =  action.type.split("_").pop().toLowerCase();

    switch (action.type) {
        case `REQUEST_${name.toUpperCase()}`:
            return  { 
                ...state,
                [name]: {
                    ...state[name],
                    isFetching: true,
                },
            }

        case `FAILED_${name.toUpperCase()}`: 
            return {
                ...state,
                [name]: {
                    ...state[name],
                    isFetching: false,
                    isError: true,
                }
            }

        case `RECEIVE_${name.toUpperCase()}`:
            return  { 
                ...state,
                [name]: {
                    ...state[name],
                    isFetching: action.isFetching,
                    list: action.payload,
                    receivedAt: action.receivedAt
                }
            }

        case `GET_${name.toUpperCase()}`: 
            return {
                ...state,
                [name]: {
                    ...state[name],
                    item: action.item,
                    isFetching: action.isFetching,
                }
            }
        case `STATE_${name.toUpperCase()}`: 
            var fieldName = action.payload.fieldName.startsWith('_')
            if(fieldName){
                state[name].tmp_state.addresses = { ...state[name].tmp_state.addresses , [ action.payload.fieldName ] : action.payload.value }
            }else{
                state[name].tmp_state = { ...state[name].tmp_state, [ action.payload.fieldName ] : action.payload.value }
            }
            return {
                ...state,
                [name]: {
                    ...state[name]
                }
            }
            
        case `CREATE_${name.toUpperCase()}`:
            return {
                ...state,
                [name]: {
                    ...state[name],
                    isFetching: action.isFetching,
                    tmp_state: initialState[name].tmp_state,
                    list: [ ...state[name].list, action.item ]
                }
            }
        default:
            return state;
    }
}
  

// manager/src/redux/HOC/getListAction.js


import axios from 'axios';
import { API_ENDPOINT, apiCall } from '../../api/constant'
import { requestData, requestFailed  } from './'

// TMP DATA
// import contacts from '../../FAKE_DATA/contacts.json'

// GET FULL LIST OF CLIENT
export function getItemList( actionType ){

    return dispatch => {

        dispatch(requestData(actionType))

        axios.get(`${API_ENDPOINT}${apiCall(actionType).endPoints.get}`, {
          method: 'GET',
          mode: 'cors',
          headers: {
              'x-access-token': localStorage.getItem('token')
          }
        })
        .then(function (response) { 
            return response.data
        }) 
        .then( res => {
          if(res.success){
              dispatch(receiveItems(actionType, res.payload ))  
              }else{
                dispatch(requestFailed(actionType))
              }
        })              
    }
}

function receiveItems(actionType, items) {
  return {
    type: `RECEIVE_${actionType}`,
    payload: items,
    receivedAt: Date.now()
  }
}

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

Ваш код работает, и я думаю, в этом нет ничего плохого.Я бы сделал немного по-другому.Я бы обернул этот редуктор в функцию и передал бы имя среза состояния, о котором будет заботиться редуктор, и начальное состояние, например:

const makeReducer = (name, initialState) => (state = initialState, action) => {

    var actionType = name.toUpperCase();

    switch (action.type) {
        case `REQUEST_${actionType}`:
            return  { 
                ...state,
                [name]: {
                    ...state[name],
                    isFetching: true,
                },
            }
        // the rest, replace constants accordingly

}

Тогда основным редуктором будет:

export default combineReducers({
      contact: makeReducer("contact", initialState.contact),
      expense: makeReducer("expense", initialState.expense),
      service: makeReducer("service", initialState.service),
      product: makeReducer("product", initialState.product)
});

В разных случаях вы можете использовать combReducers для повторного использования логики редуктора.Проверьте документы по редуксу: https://redux.js.org/recipes/structuringreducers/reusingreducerlogic.

0 голосов
/ 30 сентября 2018

Разделить редуктор на baseReducer - редуктор, который мы хотим использовать повторно, и default - редуктор, который применяется baseReducer к каждому срезу состояния.

class BaseState {
  tmp_state = {};
  item = null;
  receivedAt = null;
  isFetching = false;
  isError = false;
  list = []
}

export const baseReducer = (state = new BaseState(), action) => {
  switch (action.payload.subtype) {
    case `REQUEST`:
      return {
        ...state,
        isFetching: true,
      }
    case `FAILED`:   /* code */
    case `RECEIVE`:  /* code */
    case `GET`:      /* code */
    case `STATE`:    /* code */
    case `CREATE`:   /* code */
    default:         /* code */

  }
}

class InitialState = {
  contact = new BaseState();
  expense = new BaseState();
  service = new BaseState();
  product = new BaseState();
}

export default (state = new InitialState(), action) => {
  switch (action.type) {
    case 'CONTACT':
      return {
        ...state,
        contact: baseReducer(state.contact, action)
      }
    case 'EXPENSE': /* the same */
    case 'SERVICE': /* the same */
    case 'PRODUCT': /* the same */
    default: return state;
  }
}

Мы можем обобщить далее defaultредуктор, если у нас много товара.

const smartCompose = mapActionTypeToState => (state, action) => {
  const stateSlice = mapActionTypeToState[action.type];
  if (!stateSlice) return state;

  return {
    ...state,
    [stateSlice]: baseReducer(state.contact, action),
  }
}

const mapActionTypeToState = {
  CONTACT: 'contact',
  EXPENSE: 'expense',
  SERVICE: 'service',
  PRODUCE: 'produce',
};

export const defaultReducer = smartCompose(mapActionTypeToState);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...