如何取消从reducer发出的已调度axios请求



我在组件中使用了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
}, [])

下面是我的切片文件中的reducer,它从我的服务文件中导入函数。

// 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);
}
}
);

下面是我的服务文件中的函数

// 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查询,React查询,或SWR。它们处理了异步获取数据的常见模式,而不需要您自己编写各种片和reducer,并添加了有用的特性,如缓存、重试等。

让你的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 ))

当使用useEffect加载数据时,使用AbortController来避免内存泄漏是个好主意,这将在调度请求期间可能出现错误时非常有用,以避免在错误发生时请求仍在运行,这里有一个示例,如果状态由于任何原因没有加载,您可以使用它来取消请求。

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>;
};

最新更新