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

Ars*_*tch 9 javascript reactjs redux redux-thunk

我已经React和我一起工作Redux了大约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;
  }
};
Run Code Online (Sandbox Code Playgroud)

上面的代码显示的结构userusers duck这将是其他鸭几乎相同.

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

lux*_*lux 3

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

我从未在 Redux 中实现过reducks结构,但我曾一度发现自己在管理域实体(例如人员、订单、产品等)时生成相同的操作、reducer 等。

例如,我似乎总是关心:

  1. 我们当前正在获取实体吗?isFetching
  2. 获取实体时是否出现任何错误?error
  3. 该实体的实际数据是什么?data
  4. 上次获取实体是什么时候?lastUpdated

此外,域实体一直在添加,因此不断复制和粘贴减速器/操作并不理想。我们需要一种在 Redux 中动态存储数据的方法,并且我们希望该数据始终附加到isFetching和 等属性lastUpdated

{
  "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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

那么,如果我们发出一个带有字符串文字的操作,并将其用作 Redux 中的键(例如products, orders)呢?这样,我们就可以发出任何可用的有效操作类型(FETCH_REQUEST等),并且我们只需要更新entity密钥,这将自动为我们在 Store 中开辟空间:

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()
});
Run Code Online (Sandbox Code Playgroud)

结果状态

{
  "entities": {
    "products": {
      "isFetching" : false,
      "lastUpdated": 1526746314736,
      "data"       : [{id: 1}]
      "error"      : null
    },
    "orders": {
      "isFetching" : false,
      "lastUpdated": 1526746314943,
      "data"       : [{id: 2}, {id: 3}]
      "error"      : null
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

通用实体减速器

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;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

实体缩减器

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
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

dispatch({type: "FETCH_REQUEST", entity: "foo"});
dispatch({type: "FETCH_REQUEST", entity: "bar"});
dispatch({type: "FETCH_REQUEST", entity: "baz"});
Run Code Online (Sandbox Code Playgroud)

结果状态

{
  "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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果这看起来很有趣,我确实编写了一个小库(插件!),它的功能与上面描述的完全一样:

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

也就是说,我并不是在推动该库,我只是想描述我遇到的问题所采取的方法。您的操作集可能完全不同,在这种情况下,您仍然可以实现通用模式,但显然减速器的行为会有所不同。