在 React 功能组件中取消异步 axios 请求的正确方法

IT技术 javascript reactjs axios
2021-04-26 01:54:16

在 React 功能组件中取消异步请求的正确方法是什么?

我有一个脚本,它在加载时(或在某些用户操作下)从 API 请求数据,但是如果正在执行此过程并且用户导航离开,则会导致以下警告:

警告:无法对卸载的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。

我读过的大部分内容都使用基于类的组件AbortControllercomponentDidUnmount方法解决了这个问题而我的 React 应用程序中有一个功能组件,它使用 Axois 向 API 发出异步数据请求。

该函数驻留在useEffect功能组件中的一个钩子中,以确保该函数在组件呈现时运行:

  useEffect(() => {
    loadFields();
  }, [loadFields]);

这是它调用的函数:

  const loadFields = useCallback(async () => {
    setIsLoading(true);
    try {
      await fetchFields(
        fieldsDispatch,
        user.client.id,
        user.token,
        user.client.directory
      );
      setVisibility(settingsDispatch, user.client.id, user.settings);
      setIsLoading(false);
    } catch (error) {
      setIsLoading(false);
    }
  }, [
    fieldsDispatch,
    user.client.id,
    user.token,
    user.client.directory,
    settingsDispatch,
    user.settings,
  ]);

这是触发的 axios 请求:


async function fetchFields(dispatch, clientId, token, folder) {
  try {
    const response = await api.get(clientId + "/fields", {
      headers: { Authorization: "Bearer " + token },
    });

    // do something with the response

  } catch (e) {
    handleRequestError(e, "Failed fetching fields: ");
  }
}

注意:api变量是对axios.create对象的引用

3个回答

取消fetch操作axios

  1. 使用给定的源令牌取消请求
  2. 确保在卸载后不要更改组件状态

广告 1.)

axios 自带取消 API:

const source = axios.CancelToken.source();
axios.get('/user/12345', { cancelToken: source.token })
source.cancel(); // invoke to cancel request

您可以通过停止不再需要的异步请求来使用它来优化性能。使用本机浏览器fetchAPI,AbortController将改为使用。

广告 2.)

这将停止警告"Warning: Can't perform a React state update on an unmounted component."例如,您不能调用setState已经卸载的组件。是一个示例 Hook 强制和封装提到的约束。


例子: useAxiosFetch

我们可以将这两个步骤合并到自定义 Hook 中:

function useAxiosFetch(url, { onFetched, onError, onCanceled }) {
  React.useEffect(() => {
    const source = axios.CancelToken.source();
    let isMounted = true;
    axios
      .get(url, { cancelToken: source.token })
      .then(res => { if (isMounted) onFetched(res); })
      .catch(err => {
        if (!isMounted) return; // comp already unmounted, nothing to do
        if (axios.isCancel(err)) onCanceled(err);
        else onError(err);
      });

    return () => {
      isMounted = false;
      source.cancel();
    };
  }, [url, onFetched, onError, onCanceled]);
}

useEffect有一个您可以使用的返回选项。它的行为(几乎)与 componentDidUnmount 相同。

useEffect(() => {
  // Your axios call

  return () => {
    // Your abortController
  }
}, []);

您可以使用 lodash.debounce 并尝试以下步骤

第 1 步:

inside constructor:
this.state{
 cancelToken: axios.CancelToken,
 cancel: undefined,
}
this.doDebouncedTableScroll = debounce(this.onScroll, 100);

第二步:在使用 axios 的函数内部添加:

if (this.state.cancel !== undefined) {
                cancel();
            }

第 3 步:

 onScroll = ()=>{
    axiosInstance()
         .post(`xxxxxxx`)
              , {data}, {
                  cancelToken: new cancelToken(function executor(c) {
                         this.setState({ cancel: c });
                        })
                   })
                    .then((response) => {
    
                         }