使用 axios 的 redux-thunk 的通用数据加载器

Posted

技术标签:

【中文标题】使用 axios 的 redux-thunk 的通用数据加载器【英文标题】:Generic dataloader for redux-thunk using axios 【发布时间】:2017-10-31 16:06:40 【问题描述】:

我正在开发一个有很多异步操作的应用程序。我想使用 redux-saga,但大多数人坚持要继续使用 redux-thunk。在 redux-thunk 中,在每个动作中,我们必须使用 then、dispatch、catch 等进行异步操作。这使得看起来动作如此混乱,并且会重复大量代码。我想创建一个通用的 dataLoader 来使用 redux-thunk 和 axios 但不能同时考虑 post(可能是令牌)和 get 选项。

这是我的尝试:

export class Company 
/**
* Generic api data loader
*/
static dataLoader(apiUri, onSuccess, onError, data, ...actionArguments) 
  const requestURL = `$API_BASE$apiuri`;
  try 
    let options;
    if (data !== undefined) 
      // if we have data to post
      options = 
      method: 'POST',
      url: requestURL,
      body: JSON.stringify(data),
      headers: 
         'Content-Type': 'application/json',
         'X-Requested-With': 'XMLHttpRequest',
      ,
      ;
    
  
  return function(dispatch) 
    axios(options)
    .then(response => 
      dispatch(
        type: onSucess,
        payload: response.data
      );
    )
    .catch(error => 
      dispatch( type: onError, payload: err);
    );
  


static get(apiUri, onSuccess, onError, ...actionArguments) 
    return this.dataLoader(apiUri, onSuccess, onError, undefined, ...actionArguments);
  

  /*
   * Shorthand POST function
   */
  static post(apiUri, onSuccess, onError, data, ...actionArguments) 
    return this.dataLoader(apiUri, onSuccess, onError, data, ...actionArguments);
  


我想将以下代码转换为进一步的代码:

export function showResultofApartment() 
  return (dispatch) => 
    dispatch( type: 'APARTMENT_FETCH_START' );
    const token = localStorage.getItem('token');
    return axios.get(`$API_URL/newoffers/apartment/`)
    .then((response) => 
      console.log('response apart', response.data);
        dispatch( type: 'APARTMENT_FETCH_SUCCESS', payload: response.data );
    )
    .catch((err) => 
      dispatch( type: 'APARTMENT_FETCH_FAILURE', payload: err );
    );
  ;

这样或比这更有效率:

export function showResultofApartment() 
  return(dispatch) => 
    dispatch( type: APARTMENT_FETCH_START );
    const token = localStorage.getItem('token');
    return Company.get('/apartments', APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
    // if post then Company.post('/apartment', APARTMENT_POST_SUCCESS, APARTMENT_POST_ERROR, data)
  

这种方式只考虑发布请求(如果数据!==未定义)。我应该如何有效地处理获取和发布?

【问题讨论】:

不要造成更多混乱,我觉得 redux-saga 对你来说更优雅,以防你有多个同步或异步 axios 请求要处理。如果您有兴趣,我可以发布我们公司遵循的模式。 如果我知道你使用的模式,我会很感激你,但同时我希望这个项目使用 redux-thunk。这将是我们最后一次使用 redux-thunk。要继续这个项目,我必须坚持使用 thunk。你能帮我解决一下关于通用数据加载器的问题吗? 【参考方案1】:

好吧,你为什么不这样处理:

公司.js

import  merge  from 'lodash';
import axios from 'axios';

function getHeaders() 
  return 
   'Content-Type': 'application/json'
 ;


export class Company 

static callAPI(endpoint, extendedOptions, onSuccess, onError) 
  const initalHttpData = 
    method: 'GET', // By default it's GET in case you didnt specify anything
    headers: getHeaders(),
    url: `$API_BASE$endpoint`
  ;

  // merge takes care of replacing or adding the specific key's provided via the extendedOptions
  const options = merge(initalHttpData, extendedOptions);

  // Fire the request for the prepared options.
  let request = axios(options);

  // The request once fired, needs it's success handler and error handler.
  return function(dispatch) 
    request
    .then(response => 
      dispatch(
        type: onSucess,
        payload: response.data
      );
    )
    .catch(error => 
      dispatch( type: onError, payload: err);
    );
  
;

那么我们就可以使用action来专门把东西传递给这个api util:

GET API 调用:

// GET Action
export function showResultofApartment() 
  return (dispatch) => 
    dispatch( type: APARTMENT_FETCH_START );
    const token = localStorage.getItem('token');

    // FOR GET API
    return Company.callApi('/apartments', , APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
  

POST API 调用:

// POST Action
export function showResultOfAppartmentPost() 
  return (dispatch) => 
    dispatch( type: APARTMENT_FETCH_START );
    const token = localStorage.getItem('token');

    // This will merge, essentially replace the method=GET once it gets called.
    const extendedOptions = 
      method: 'POST',
      body: JSON.stringify(data),
      headers: 
         'X-Requested-With': 'XMLHttpRequest',
      
    

    // FOR GET API
    return Company.callApi('/apartments', extendedOptions, APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);

因此,给出动作,定义它自己的一组 API 主体或请求。

【讨论】:

衷心感谢您的帮助,感谢您的努力。一个问题,是不是合并改变了对象?我们不能只做 Object.assign(, initalHttpData, extendedOptions) 吗? @Serenity 我使用了 Lodash 库:lodash.com/docs/#merge,它本质上是具有深度比较的对象分配 我尝试使用 Object.assign() 但收到错误 404 not found Cannot OPTIONS /api/.这是由于 Object.assign() 导致的错误吗? @Serenity 与 Object.assign 无关。我猜你的 CORS 选项不正确。为/api/ 触发的OPTIONS 无法访问您正在查询的api。请问api端点是否正确? 我在控制台extendedOptions body: "["asdas","asdasd","1",["preview":"blob:http://localhost:3000/7a151a53-aa07-4a2c-a0b4-47a2860f2042"]]" headers:Object method: 'POST'时得到这个

以上是关于使用 axios 的 redux-thunk 的通用数据加载器的主要内容,如果未能解决你的问题,请参考以下文章

使用 Redux-Thunk / Axios 从 onUploadProgress 事件调度操作

如何使用“redux-thunk”调用 ajax?

Redux axios 请求取消

取消路由更改请求 (React/Redux/Axios)

使用 Redux Thunk 和 Axios 测试 Action Creator

Redux Thunk操作,axios返回多个值