Redux:从 Web 服务访问状态的正确方法?

Redux: Proper way to access state from web service?

我已经概述了几种可能从 Web 服务访问状态的方法,但我不知道在 react-redux 应用程序中哪一种是正确的,或者下面是否列出了正确的方法。

上下文:

最初,我有一个 API.js 文件作为 Web 服务的基础。然后我会将其导入到我的动作文件中。一切顺利 ,直到我需要从 API.js 访问状态 (更具体地说,我的 header 需要的状态中的网络令牌)。我尝试导入我的商店,但它返回 undefined。然后我意识到我有一个 循环依赖:

api -> store -> reducers -> components -> actions

自定义中间件


我想知道这是否可以接受。我放弃了 API.js。我用它来自动修改具有特定操作类型的传出网络调用。这是我的中间件堆栈的样子:

const middleware = applyMiddleware(
    myCustomModifyRequestMiddleware,
    thunk,
    . . .

myCustomModifyRequestMiddleware基本上是这样的:

 const myCustomModifyRequestMiddleware = store => next => action {
     if(action.type === 'MODIFY_ME') {

         //Dispatch an FSA
         store.dispatch({
             type: action.payload.actual_type,
             payload: axios.create({ . . .
             meta: action.meta
         })
     }

     return next(action)
 }

现在我的中间件中有了业务逻辑!

然后我可以有一个名为 API_ActionCreator 的动作创建器。但是,嘿,如果我只是想使用动作创建器,为什么不...

赞一下


使用 thunks 我可能只需要 API_ActionCreator.js:

const apiActionCreator = (actual_type, url, data . . .) {
    return (dispatch, store) {
        //Now I can get the state...
        store.getState()

        //Return an FSA
        return { 
            type: actual_type,
            payload: axios.create...

现在我可以将我的 API_ActionCreator 导入到我的操作中,而无需任何循环依赖。

正在订阅商店?

另一种方法是让 Web 服务是有状态的;订阅 storeweb service 中的商店,如果我能以某种方式避免 运行 在我的操作中调用我的 Web 服务时陷入循环依赖。

TLDR; 当然,这都是实验性的,尽管我能够让中间件工作。

我不知道哪一个是最可行的方法,是否有更 redux-ish 的方法来做到这一点?

Thunk 动作创建者和集中式中间件都是在 Redux 中管理 API 调用同时访问 dispatch 和 getState` 的标准方法。两者都可以。

有关详细信息,请参阅 Dan 在 and , as well as the other articles in the Redux Side Effects section of my React/Redux links list. You might also be interested in the list of Redux middleware for making network requests in my Redux addons catalog 上的回答。

我想分享一种方法,我们在遇到需要访问 auth 令牌 的问题时使用的方法,同时为之间的获取请求创建 header 选项不同的服务。

我们最终使用单例模式创建了一个 API 服务,该服务将负责:

  • 在整个使用过程中保持单个实例
  • 持有 _token 等属性供所有服务使用
  • 公开一个获取方法,服务可以使用该方法使用令牌设置默认 headers 并发出请求

这是服务的样子:

let _instance = null;

class ApiService {
  static getInstance() {
    if (_instance === null) {
      _instance = new ApiService();
    }

    return _instance;
  }

  setToken(token) {
    this._token = token;
  }

  defaultHeaders(immediateHeaders) {
    const headers = {
      'Content-type': 'application/json',
      ...immediateHeaders,
    };

    if (this._token) {
      headers['Authorization'] = `Bearer ${this._token}`;
    }

    return headers;
  }

  fetch(url, options) {
    const headers = this.defaultHeaders();

    const opts = {
      ...options,
      headers,
    };

    return fetch(url, opts);
  }
}

export default ApiService;

用法

使用此方法时,要做的第一件事是在状态处理程序期间在服务上设置令牌 属性,当令牌作为状态可用时公开给令牌。

例如,在身份验证状态处理程序中设置令牌是一个好的开始,因为令牌将从状态可用,例如 state.auth.token

为此,在您的 登录成功操作 中,无论是作为 thunk 还是 saga,在将用户重定向到私有路由或可能依赖于获取的特定组件之前设置令牌:

ApiService.getInstance().setToken(token);

在页面刷新时,如果令牌未定义,请确保它可以是来自 initialState 的 re-hydrated。

例如在配置商店的Root或App组件中添加此方法并可以访问初始状态。

if (initialState.auth.token) {
  ApiService.getInstance().setToken(initialState.auth.token);
}

当令牌在 ApiService 实例上设置为 属性 时,使用令牌从任何服务发出获取请求非常简单。

只需导入 ApiService 并正常进行提取,但使用 public 提取方法。

进行提取时,正常传递 URL 和任何相关选项,例如方法或 Body,但默认情况下使用 auth 设置的 header 除外令牌。

import ApiService from './api.service';

// Get the API service instance

const api = ApiService.getInstance();

export default () => ({

  fetchWorkspaces: async () => {

    const response = await api.fetch(url);

    const workspaces = await response.json();

    return workspaces;

  },
})

希望对您有所帮助!