如何避免redux中的重复代码(ducks方法)?

时间:2018-05-18 14:29:40

标签: javascript reactjs redux redux-thunk

我已经与ReactRedux合作了大约3年。 我还使用redux-thunk作为异步内容。

我非常爱他们,但最近我注意到我项目中几乎所有的鸭子都使用相同的动作,减速器,选择器等结构。

例如 - 您有一个应用程序,它有一些用户和事务(或类似)列表,项目详细信息和编辑功能。 所有这些列表或项目都有自己的鸭子(动作,缩减器,选择器等)。

以下代码将更清楚地显示问题:

// ACTIONS

const const setUser = user => ({
  type: types.SET_USER,
  payload: user,
});

const cleanUser = () => ({ type: types.CLEAN_USER });

const fetchUser = userId => dispatch =>
  dispatch(fetchApi(userRequests.get(userId)))
    .then(response => dispatch(setUser(response)))
    .catch(error => showNotification(error));

// delete, update, etc... user actions

// REDUCER

const userReducer = (state = null, action) => {
  switch (action.type) {
    case types.SET_GROUP_ITEM:
      return action.payload;
    case types.CLEAN_GROUP_ITEM:
      return null;
    default:
      return state;
  }
};

上面的代码显示了来自user的{​​{1}}的结构,对于其他鸭子来说几乎相同。

有没有办法减少重复代码? 谢谢您的提前!

1 个答案:

答案 0 :(得分:3)

  

我注意到我项目中几乎所有的鸭子都使用相同的鸭子   行动结构,减速器,选择器等。

我从未在Redux中实现reducks结构,但在管理我的域实体时(例如人员,订单,我在某一时刻发现自己会生成相同的操作,缩减器等)产品等)。

例如,我似乎总是在乎:

  1. 我们目前是否正在获取该实体? isFetching
  2. 获取实体是否有任何错误? error
  3. 实体的实际数据是什么? data
  4. 该实体上次获取的时间是什么时候? lastUpdated
  5. 此外,域实体一直在增加,因此不断复制和粘贴reducer / actions并不理想。我们需要一种在Redux中动态存储数据的方法,并且我们希望将始终的数据附加到isFetchinglastUpdated等属性。< / p>

    {
      "entities": {
        <SOME_ENTITY>: {
          "isFetching" : null    // Am I fetching?
          "lastUpdated": null    // When was I last fetched?
          "data"       : null    // Here's my data!
          "error"      : null    // Error during fetching
        }
      }
    }
    

    那么,如果我们发布了一个字符串文字的动作,该动作将用作Redux中的一个键(例如productsorders)?这样,我们可以发布任何可用的有效操作类型(FETCH_REQUEST等),我们只需要更新entity键,这将自动为我们分割商店中的空间:

    dispatch({
        entity     : "products",
        type       : "FETCH_SUCCESS", 
        data       : [{id: 1}],
        lastUpdated: Date.now()
    });
    
    dispatch({
        entity    : "orders",
        type      : "FETCH_SUCCESS",
        data      : [{id: 2}, {id: 3}],
        lastUpdated: Date.now()
    });
    

    结果状态

    {
      "entities": {
        "products": {
          "isFetching" : false,
          "lastUpdated": 1526746314736,
          "data"       : [{id: 1}]
          "error"      : null
        },
        "orders": {
          "isFetching" : false,
          "lastUpdated": 1526746314943,
          "data"       : [{id: 2}, {id: 3}]
          "error"      : null
        }
      }
    }
    

    通用实体减速器

    function entities (state = {}, action) {
        switch (action.type) {
            case FETCH_SUCCESS: // fall through
            case FETCH_FAILURE: // fall through
            case FETCH_REQUEST: {
                return Object.assign({}, state, {
                    [action.entity]: entity(
                        state[action.entity],
                        action
                    )
                });
            }
            default: {
                return state;
            }
        }
    };
    

    实体减少器

    const INITIAL_ENTITY_STATE = {
        isFetching : false,
        lastUpdated: null,
        data       : null,
        error      : null
    };
    
    function entity (state = INITIAL_ENTITY_STATE, action) {
        switch (action.type) {
            case FETCH_REQUEST: {
                return Object.assign({}, state, {
                    isFetching: true,
                    error     : null
                });
            }
            case FETCH_SUCCESS: {
                return Object.assign({}, state, {
                    isFetching : false,
                    lastUpdated: action.lastUpdated,
                    data       : action.data,
                    error      : null
                });
            }
            case FETCH_FAILURE: {
                return Object.assign({}, state, {
                    isFetching : false,
                    lastUpdated: action.lastUpdated,
                    data       : null,
                    error      : action.error
                });
            }
        }
    }
    

    同样,通过使用通用缩减器,我们可以动态地将任何我们喜欢的内容存储到Redux中,因为我们使用下面的entity字符串作为Redux中的键

    dispatch({type: "FETCH_REQUEST", entity: "foo"});
    dispatch({type: "FETCH_REQUEST", entity: "bar"});
    dispatch({type: "FETCH_REQUEST", entity: "baz"});
    

    结果状态

    {
      "entities": {
        "foo": {
          "isFetching": true,
          "error": null,
          "lastUpdated": null,
          "data": null
        },
        "bar": {
          "isFetching": true,
          "error": null,
          "lastUpdated": null,
          "data": null
        },
        "baz": {
          "isFetching": false,
          "error": null,
          "lastUpdated": null,
          "data": null
        }
      }
    }
    

    如果这看起来很有意思,我确实写了一个小的lib(插件!),它完全符合上面的描述:

    现场演示: http://mikechabot.github.io/react-boilerplate/dist/

    那就是说,我没有推动那个lib,我只是想描述一下我遇到问题的方法。你的动作集可能完全不同,在这种情况下,你仍然可以实现通用模式,但显然减速器的行为会有所不同。