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

时间:2017-07-14 10:53:54

标签: javascript redux react-redux redux-thunk redux-middleware

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

上下文

最初,我有一个API.js文件,它充当了Web服务的基础。然后我会将其导入我的动作文件。这很顺利直到我需要从API.js访问状态(更具体地说,是我的标题所需的状态中的Web令牌)。我尝试导入我的商店,但它返回了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的动作创建者。但是,嘿,如果我只是想使用动作创作者,为什么不只是......

Thunk it

使用thunk我可能只有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; 当然,这都是实验性的,尽管我能够让中间件工作。

我不知道哪一种是最可行的方法,是否有更多的减少方法可以做到这一点?

2 个答案:

答案 0 :(得分:1)

Thunk动作创建者和集中式中间件都是在Redux中管理API调用的同时访问dispatch和getState`的标准方法。其中任何一个都没问题。

有关详细信息,请参阅Dan在managing async behavior in Reduxwhy thunks and other middleware are useful for async work上的答案,以及我React/Redux links list的Redux副作用部分中的其他文章。您可能还对Redux middleware for making network requests中的Redux addons catalog列表感兴趣。

答案 1 :(得分:0)

我想分享我们在为不同服务之间的抓取请求创建标头选项时遇到需要访问身份验证令牌的问题时使用的方法。

我们最终使用Singleton模式来创建一个负责以下的API服务:

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

这是服务的样子:

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;

<强>用法

使用这种方法时,首先要做的是在状态处理程序期间在服务上设置令牌属性,当状态处理程序作为状态可用时显示。

E.g在认证状态处理程序中设置令牌是一个良好的开端,因为令牌可以从状态中获得,例如 state.auth.token

要执行此操作,请在登录成功操作内部以thunk或saga设置令牌,然后再将用户重定向到可能取决于提取的私有路由或特定组件:

ApiService.getInstance().setToken(token);

在页面刷新时,如果未定义标记,请确保它可以从initialState重新水合。

E.g在配置商店的Root或App组件中添加此方法,并且可以访问初始状态。

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

当令牌被设置为ApiService实例上的属性时,使用令牌从任何服务获取提取请求都很简单。

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

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

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;

  },
})

希望这有用!