简体   繁体   English

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

[英]Cancel previous fetch request with redux-thunk

Background of the Problem:问题背景:

I am building a React/Redux app that uses redux-thunk and wretch (a fetch wrapper) to handle asynchronous requests.我正在构建一个 React/Redux 应用程序,它使用 redux-thunk 和 wretch(一个获取包装器)来处理异步请求。

I have a few search actions that can vary significantly in their load times, causing undesirable behavior.我有一些搜索操作的加载时间可能会有很大差异,从而导致不良行为。

I have looked into using AbortController(), but it's either cancelling all my requests outright, or failing to cancel the previous request.我曾考虑使用 AbortController(),但它要么彻底取消我的所有请求,要么未能取消前一个请求。

example problem:示例问题:

  • Request a search for "JOHN", then request a search for "JOHNSON".请求搜索“JOHN”,然后请求搜索“JOHNSON”。
  • Results for "JOHNSON" return first, and then results for "JOHN" return later and overwrite the "JOHNSON" results. “JOHNSON”的结果首先返回,然后“JOHN”的结果稍后返回并覆盖“JOHNSON”的结果。

Goal:目标:

Initiating a request should abort previous pending requests.发起请求应该中止之前的未决请求。

example desired behavior:示例所需的行为:

  • Request a search for "JOHN", then request a search for "JOHNSON".请求搜索“JOHN”,然后请求搜索“JOHNSON”。
  • Upon initiating the request for "JOHNSON", the pending request for "JOHN" is aborted.在启动对“JOHNSON”的请求时,中止对“JOHN”的未决请求。

Code:代码:

actions.js动作.js

The fetchData action gets called via an onClick or by other functions.通过 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实用程序/ request.js

This is wretch import.这是可怜的进口。 Wretch is a fetch wrapper so it should function similarly to fetch. 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;

Attempt:试图:

I've tried using wretch's .signal() parameter with an AbortController(), calling .abort() after the request, but that aborts all requests, causing my app to break.我试过将 wretch 的 .signal() 参数与 AbortController() 一起使用,在请求后调用 .abort() ,但这会中止所有请求,导致我的应用程序中断。 Example below:下面的例子:

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;

I've tried moving the logic around to various places, but it seems abort all actions or abort none of them.我试过将逻辑移到不同的地方,但它似乎中止所有动作或不中止任何动作。

Any advice as to how to go about this would be appreciated, this is critical for my team.任何有关如何解决此问题的建议将不胜感激,这对我的团队至关重要。

Thank you谢谢

I feel pretty silly right now, but this is what it took to get it working.我现在觉得很傻,但这就是让它工作所需要的。

Solution Steps:解决步骤:

  • Set an AbortController to the initialState of the reducer将 AbortController 设置为 reducer 的 initialState

reducer.js减速器.js

export default (state = {
  controller: new AbortController(),
}, action) => {
  switch (action.type) {
    ...
  • Get the AbortController from the state, at the beginning of the fetch action and abort it.在获取操作开始时从状态中获取 AbortController 并中止它。
  • Create a new AbortController and pass it into the requestData action.创建一个新的 AbortController 并将其传递给 requestData 操作。
  • Pass the new AbortController into the signal() param of the wretch call.将新的 AbortController 传递到 wretch 调用的signal()参数中。

actions.js动作.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,
  }
}

In the reducer, for the case of the requestData action, set the new AbortController to the state.在reducer 中,对于requestData 动作的情况,将新的AbortController 设置为状态。

reducer.js减速器.js

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

There's some additional functionality with wretch, an .onAbort() param, that allows you to dispatch other actions when the request is aborted. wretch 有一些附加功能,一个.onAbort()参数,允许您在请求中止时分派其他操作。 I haven't coded that out yet, but I figured I'd include the info for anyone else struggling with this.我还没有把它编码出来,但我想我会为其他任何为此苦苦挣扎的人提供信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM