繁体   English   中英

如何取消 reducer 发送的 axios 请求

[英]How to cancel a dispatched axios request from reducer

我在我的组件中使用了 useEffects 来加载数据,组件安装的那一刻。 但我正在尝试通过避免任何内存泄漏来优化我的代码。 为实现这一点,如果组件卸载,我将尝试使用 AbortController 在任何情况下取消任何请求。 像这样的东西

useEffect(() => {
         let abortController;
        (async () {
             abortController = new AbortController();
             let signal = abortController.signal;    

             // the signal is passed into the request(s) we want to abort using this controller
             const { data } = await axios.get(
                 'https://random-data-api.com/api/company/random_company',
                 { signal: signal }
             );
             setCompany(data);
        })();

        return () => abortController.abort();
    }, []);

但我发现很难实现这一点,因为我的 axios 请求位于服务文件中,该文件由切片文件中的缩减器调用。 下面是我的组件的 useEffect。

// Component.js
import { bookDetails } from '../../features/user/userSlice'
//import reducer from my slice file
.
.
// code 
useEffect(() => {
        let mounted = true
        if (mounted) {
            dispatch(bookDetails(bookId))
        }
        return () => mounted = false
    }, [])

下面是我的切片文件中的减速器,它从我的服务文件中导入 function。

// userSlice.js
import userService from "./userService";

export const bookDetails = createAsyncThunk(
  "user/book",
  async (id, thunkAPI) => {
    try {
      const token = thunkAPI.getState().auth.user.token;
      return await userService.bookDetails({ id, token });
    } catch (error) {
      const message =
        (error.response &&
          error.response.data &&
          error.response.data.message) ||
        error.message ||
        error.toString();
      return thunkAPI.rejectWithValue(message);
    }
  }
);

下面是我的服务文件中的 function

// userService.js
const bookDetails = async ({ id, token }) => {
  const config = {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  };
  const response = await axios.get(API_URL + `/book/${id}`, config);
  return response.data;
};

我想取消此请求,以防我的组件从 useEffect 卸载。 请帮忙。 提前致谢。

由于您使用的是 Redux Toolkit 的 createAsyncThunk,因此听起来您正在寻找 createAsyncThunk 的“运行时取消”功能 也许像下面这样:

export const bookDetails = createAsyncThunk(
  "user/book",
  async (id, thunkAPI) => {
    try {
      const token = thunkAPI.getState().auth.user.token;
      return await userService.bookDetails({ id, token, signal: thunkAPI.signal });
    } catch (error) {
      return thunkAPI.rejectWithValue(getErrorMessage(error));
    }
  }
);

useEffect(() => {
  const promise = dispatch(bookDetails(bookId));
  return () => promise.abort();
}, [])

但是,如果您担心的只是获取数据,我强烈建议您查看RTK QueryReact QuerySWR 这些处理异步获取数据的常见模式,无需您自己编写各种切片和缩减程序,并添加有用的功能,如缓存、重试等。

让您的bookDetails方法接受一个名为signal的额外属性。 然后将其传递给axios方法。

const bookDetails = async ({ id, token, signal }) => {
  const config = {
    headers: {
      Authorization: `Bearer ${token}`,
    },
    signal,
  };
  const response = await axios.get(API_URL + `/book/${id}`, config);
  return response.data;
};

为此,像这样更改您的 reducer 方法:

async ({ id, signal}, thunkAPI) => {
    try {
      const token = thunkAPI.getState().auth.user.token;
      return await userService.bookDetails({ id, token, signal });
    } catch (error) {
      const message =
        (error.response &&
          error.response.data &&
          error.response.data.message) ||
        error.message ||
        error.toString();
      return thunkAPI.rejectWithValue(message);
    }
  }

最后派发这样的动作:

abortController = new AbortController();
let signal = abortController.signal;               

dispatch(bookDetails({ id: bookId, signal: signal ))

最好有一个AbortController来避免在使用useEffect加载数据时发生 memory 泄漏 当在调度请求期间可能出错时这将很有用,以避免在发生错误时请求仍在运行,这是一个示例,您可以使用它来取消请求 state 是否因任何原因未加载。

import { useEffect, useState } from 'react';

const MyComponent = () => {
  const [data, setData] = useState(null); // set state to null

  useEffect(() => {
    const controller = new AbortController(); // AbortController instance

    // Dispatch the request
    fetch('/data', { signal: controller.signal })
      .then(response => response.json()) // get response
      .then(data => setData(data)) // Updating useState
      .catch(e => { // Catch errors
        if (e.name === 'AbortError') {
          console.log("Failed to fetching data");
        }
        throw e;
      });

    // cleanup controller
    return () => {
      controller.abort();
    };
  }, []);

  // Render the component
  return <div>{data && data.message}</div>;
};

暂无
暂无

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

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