使用redux-thunk取消先前的提取请求

时间:2020-02-20 20:13:10

标签: javascript reactjs redux fetch redux-thunk

问题背景:

我正在构建一个React / Redux应用程序,该应用程序使用redux-thunk和wretch(获取包装器)来处理异步请求。

我有一些搜索操作,它们的加载时间可能有很大不同,从而导致不良行为。

我已经研究过使用AbortController(),但是它要么直接取消我的所有请求,要么无法取消先前的请求。

示例问题:

  • 请求搜索“ JOHNSON”,然后请求搜索“ JOHNSON”。
  • “ JOHNSON”的结果先返回,然后再返回“ JOHN”的结果,然后覆盖“ JOHNSON”的结果。

目标:

发起请求应中止先前的待处理请求。

所需行为示例:

  • 请求搜索“ JOHN”,然后请求搜索“ JOHNSON”。
  • 启动“ JOHNSON”请求后,对“ JOHN”的待处理请求被终止。

代码:

actions.js

通过onClick或其他功能调用fetchData操作。

import api from '../../utils/request';
export function fetchData(params) {
  return dispatch => {
    dispatch(requestData());
    return api
      .query(params)
      .url('api/data')
      .get()
      .fetchError(err => {
        console.log(err);
        dispatch(apiFail(err.toString()));
      })
      .json(response => dispatch(receiveData(response.items, response.totalItems)))
  }
}

export function requestData() {
  return {
    type: REQUEST_DATA,
    waiting: true,
  }
}

export function receiveData(items, totalItems) {
  return {
    type: RECEIVE_DATA,
    result: items,
    totalItems: totalItems,
    waiting: false,
  }
}

export function apiFail(err) {
  return {
    type: API_FAIL,
    error: err,
    waiting: false,
  }
}

utils / request.js

这是可疑的导入。 Wretch是访存包装程序,因此其功能应类似于访存。

import wretch from 'wretch';

/**
 * Handles Server Error
 * 
 * @param {object}      err HTTP Error
 * 
 * @return {undefined}  Returns undefined
 */
function handleServerError(err) {
  console.error(err);
}

const api = wretch()
  .options({ credentials: 'include', mode: 'cors' })
  .url(window.appBaseUrl || process.env.REACT_APP_API_HOST_NAME)
  .resolve(_ => _.error(handleServerError))

export default api;

尝试:

我尝试将wretch的.signal()参数与AbortController()结合使用,在请求后调用.abort(),但这会中止所有请求,导致我的应用程序中断。下面的示例:

import wretch from 'wretch';

/**
 * Handles Server Error
 * 
 * @param {object}      err HTTP Error
 * 
 * @return {undefined}  Returns undefined
 */
function handleServerError(err) {
  console.error(err);
}
const controller = new AbortController();

const api = wretch()
  .signal(controller)
  .options({ credentials: 'include', mode: 'cors' })
  .url(window.appBaseUrl || process.env.REACT_APP_API_HOST_NAME)
  .resolve(_ => _.error(handleServerError))

controller.abort();
export default api;

我曾尝试将逻辑移到各个地方,但似乎中止了所有动作或都不放弃任何动作。

任何有关如何做到这一点的建议都将受到赞赏,这对我的团队至关重要。

谢谢

1 个答案:

答案 0 :(得分:2)

我现在觉得很傻,但这就是使它工作所需的时间。

解决步骤:

  • 将AbortController设置为reducer的initialState

reducer.js

export default (state = {
  controller: new AbortController(),
}, action) => {
  switch (action.type) {
    ...
  • 在提取操作开始时从状态获取AbortController并中止它。
  • 创建一个新的AbortController并将其传递给requestData操作。
  • 将新的AbortController传递到wretch调用的signal()参数中。

actions.js

export function fetchData(params) {
  return (dispatch, getState) => {
    const { controller } = getState().reducer;
    controller.abort();

    const newController = new AbortController();
    dispatch(requestData(newController));
    return api
      .signal(newController)
      .query(params)
      .url('api/data')
      .get()
      .fetchError(err => {
        console.log(err);
        dispatch(apiFail(err.toString()));
      })
      .json(response => dispatch(receiveData(response.items, response.totalItems)))
  }
}

export function requestData(controller) {
  return {
    type: REQUEST_DATA,
    waiting: true,
    controller,
  }
}

在化简器中,对于requestData操作,将新的AbortController设置为状态。

reducer.js

case REQUEST_DATA:
  return {
    ...state,
    waiting: action.waiting,
    controller: action.controller
  };

wretch有一些附加功能,.onAbort()参数,允许您在请求中止时调度其他操作。我尚未对此进行编码,但我想我会把所有为此苦苦挣扎的人的信息都包括在内。