像 api 工厂一样为多个 Ajax 调用重用相同的 reducer 和史诗?

Reusing the same reducer & epic for multiple Ajax calls like an api factory?

是否使用相同的 reducer 来更新状态的不同部分是一种反模式?

就像我的数据缩减器有一个 GET_DATA_DONE 操作,更新 state.data 然后在另一个实例中你使用获取其他东西并调用 GET_DATA_DONE 来更新 state.somethingElse?

或者你会做类似 GET_SOMETHING_DATA_DONE 等的事情.. 多个 diff 动作做同样的事情吗? (几乎没有DRY

reducers.js

export const reducer = (state, action) => {
  switch (action.type) {
    case actions.GET_DATA_REQUESTED:
      return { ...state, isLoading: true };
    case actions.GET_DATA_DONE:
      return { ...state, isLoading: false, data: action.payload };
    case actions.GET_DATA_FAILED:
      return { ...state, isLoading: false, isError: true }
    default:
      return state;
  }
};

actions.js

export function getDataRequested() {
  return {
    type: 'GET_DATA_REQUESTED'
  };
}

export function getDataDone(data) {
  return {
    type: 'GET_DATA_DONE',
    payload: data
  };
}

export function getDataFailed(error) {
  return {
    type: 'GET_DATA_FAILED',
    payload: error
  };
};

export function getDataEpic(action$) {
  return action$.ofType(GET_DATA_REQUESTED)
    .mergeMap(action =>
      ajax.getJSON(action.url)
        .map(response => getDataDone(response))
        .catch(error => getDataFailed(error))
    );
}

构建应用程序的最佳方式是什么,getDataEpic 就像一个 api 工厂,并且从 getDataDone(response) 返回的数据可以传递给另一个 reducer 以进行更新基于动作的状态的一部分,例如使用 getDataDone reducer 的城市动作调度另一个动作,该动作使用响应更新 state.cities?

编辑:我用 rx-observable 和 redux 调用了 3 个不同的 api 制作了一个应用程序,但我最终得到了很多重复的代码并且对解决方案不满意,所以我想构建一个结构合理的应用程序

希望我说得够清楚了。

非常感谢!

我不认为它是一个 anti-pattern 如果处理它自己的状态 children,但如果使用太多,它肯定会给你带来麻烦。让它修改完全不相关的状态是肯定的anti-pattern。 According to the docs第一行一针见血

For any meaningful application, putting all your update logic into a single reducer function is quickly going to become unmaintainable.

我们不是在谈论 'all our data',而是在谈论一次 API 调用的所有数据。

设置加载标志并不太复杂,但在现实世界中,对于同时设置加载标志、错误标志和 'data' 的减速器,应用程序会变得更加复杂。这是假设我们甚至知道所请求的数据是什么。

在您的示例中,如果打算通过 reducer 创建一个 API 工厂,我们现在必须假设 API 可以 return 任意数量的不同数据结构它可能是 stringint,但如果它是深度嵌套的 Object 怎么办?您将如何访问此数据并将其与另一条数据区分开来?


假设我们有一个仅针对错误的数据结构的应用程序:

{
  "errors": {
    "byId": {
      "1": {
        "code": 500,
        "message": "There was an internal server error"
      },
      "2": {
        "code": 400,
        "message": "There was another error."
      },
      "3": {
        "code": 999,
        "message": "Wow! weird error."
      },
    },
    "all": ["1", "2", "3"]
  }
}

我可能有一个 byId reducer,它 return 是一个计算键,另一个 reducer 作为值。

byId = (state={}, action) => {
  if (action.type === 'ADD_ERROR') {
    ...state,
    [action.id]:error_reducer(state[action.id], action)
  } else {
    return state
  }
}

error_reducer 可能看起来像

errorReducer = (state={}, action) => {
  if (action.type === 'ADD_ERROR') {
    code: action.code,
    message: action.message
  } else {
    return state
  }
}

我认为让 errorReducer 处理代码和消息更有意义,因为我们知道它们都是相互包含的数据片段,而每个错误都是相互排斥的(不同的 id),因此需要它们自己的 reducer。

在处理 real-world 应用程序时, 的另一个主要优点是,当数据分离时,一个操作可以跨 MANY 我们应用程序的不同区域。当 reducer 处理多个状态时,这些绑定状态变得更难更新。


有许多不同的模式可以用于你的减速器,其中 none 是错误的但是我发现这个模式对我来说非常有效并且我已经在一个相当复杂的生产中成功地使用它应用

综上所述,您的 AJAX 函数的一种可能方法是编写一个通用操作,该操作接受包含您的调度的 object。

通过使用像 redux-thunk 这样的库,您可以执行多个分派以使用不同的数据片段更新状态的不同部分。我不会在这里解释 redux-thunk 因为我认为这超出了问题的范围。

一个示例 object 可能如下所示:

{
  getDataRequested: function () {
    return {
      type: 'GET_DATA_REQUESTED'
    };
  },
  getDataFailed: function (error) {
    return {
      type: 'GET_DATA_FAILED',
      payload: error
    };
  },
  getDataDone: function (data) {
    return {
      type: 'GET_DATA_DONE',
      payload: data
    };
  }
}

然后您可以将此 object 回调连同您的 API 端点、REST 请求类型等一起传递给您的主要 AJAX 函数。

希望对您有所帮助。