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

She*_*ixt 1 javascript reactjs axios

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

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

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

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

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

  useEffect(() => {
    loadFields();
  }, [loadFields]);
Run Code Online (Sandbox Code Playgroud)

这是它调用的函数:

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

这是触发的 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: ");
  }
}
Run Code Online (Sandbox Code Playgroud)

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

for*_*d04 5

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

您可以通过停止不再需要的异步请求来使用它来优化性能。使用本机浏览器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]);
}
Run Code Online (Sandbox Code Playgroud)

代码沙盒

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