Redux,如何重用reduce / actions?

时间:2018-09-30 03:45:55

标签: reactjs redux

我正在尝试创建HOC或至少减少reducer / action的数量。因此,我创建了一个库缩减程序来保存我的所有数据,并创建一个getItemList操作来处理每个操作。当从react componentDidMount()调用动作时,我将传递一个类似(product,user等...)的参数,该参数将知道要更新哪个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 :(得分:2)

您的代码有效,我认为它没有错。我会做些不同。我会将该减速器包装在一个函数中,并传递减速器将关心的状态片的名称和初始状态,例如:

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)
});

您可以在不同情况下使用CombineReducers重用reducer逻辑。检查redux文档:https://redux.js.org/recipes/structuringreducers/reusingreducerlogic

答案 1 :(得分:1)

将化简器拆分为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);