使用 redux-thunk 取消之前的 fetch 请求

Pep*_*Pep 3 javascript fetch reactjs redux redux-thunk

问题背景:

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

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

我曾考虑使用 AbortController(),但它要么彻底取消我的所有请求,要么未能取消前一个请求。

示例问题:

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

目标:

发起请求应该中止之前的未决请求。

示例所需的行为:

  • 请求搜索“JOHN”,然后请求搜索“JOHNSON”。
  • 在启动对“JOHNSON”的请求时,中止对“JOHN”的未决请求。

代码:

动作.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,
  }
}
Run Code Online (Sandbox Code Playgroud)

实用程序/ request.js

这是可怜的进口。Wretch 是一个 fetch 包装器,因此它的功能应该与 fetch 类似。

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;
Run Code Online (Sandbox Code Playgroud)

试图:

我试过将 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;
Run Code Online (Sandbox Code Playgroud)

我试过将逻辑移到不同的地方,但它似乎中止所有动作或不中止任何动作。

任何有关如何解决此问题的建议将不胜感激,这对我的团队至关重要。

谢谢

Pep*_*Pep 5

我现在觉得很傻,但这就是让它工作所需要的。

解决步骤:

  • 将 AbortController 设置为 reducer 的 initialState

减速器.js

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

动作.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,
  }
}
Run Code Online (Sandbox Code Playgroud)

在reducer 中,对于requestData 动作的情况,将新的AbortController 设置为状态。

减速器.js

case REQUEST_DATA:
  return {
    ...state,
    waiting: action.waiting,
    controller: action.controller
  };
Run Code Online (Sandbox Code Playgroud)

wretch 有一些附加功能,一个.onAbort()参数,允许您在请求中止时分派其他操作。我还没有把它编码出来,但我想我会为其他任何为此苦苦挣扎的人提供信息。

  • React 17.0.2 在控制台“serializedStateInvariantMiddleware.ts:222”中抛出错误,在状态中检测到不可序列化的值,路径为:company.controller。值:AbortController {signal:AbortSignal} `如果您尝试将 AbortController 实例放在那里。 (2认同)