我有一个包含 3 个图表(图形)的组件,使用 React.js 作为框架,
Highcharts
和 HighchartsReact
来创建图表。
为了将数据填充到这些图表中,我向服务器发出 3 个请求,为每个图表提供数据。
当组件安装时,它发出请求(使用 Axios),等待响应,将数据传递到图表,然后在 DOM 中显示/绘制。
应用程序的结构方式: Axios 文件:
组件:
根.jsx: 图表组件和其他页面将在该组件中显示)。 它有一个导航栏,例如,当您单击导航栏中的图表图标时,它将重定向到图表页面。 导航栏始终存在。 使用react-router-dom来处理重定向。
问题: 在发出请求时,如果我访问另一个页面(同一应用程序),并且组件卸载,则会收到错误: 警告:无法对已卸载的组件执行 React 状态更新。这是一个空操作,但它表明应用程序中存在内存泄漏。要修复此问题,请取消 useEffect 清理函数中的所有订阅和异步任务。
我该如何处理这个问题? 我应该:
我尝试了 abortController 但无法使其工作。 老实说,我什至不知道在我的情况下使用它是否有意义。
// axios utils
export const apiGetCall = (url) => axiosInstance.get(url);
// One of the function to get data from server and pass to chart
useEffect(() => {
const fetchData = async () => {
try {
const response = await apiGetCall("/departments");
const departmentsData = response.data.departments;
// Update the state with the fetched department data
setDepartmentData(departmentsData);
// Move the contents of pieChartOptions to the useState hook
setPieChartOptions({
// Department data
});
} catch (err) {
console.log(err);
}
};
fetchDepartmentData();
}, []);
我刚刚插入了相关代码。图表显示等工作正常。 我只需要帮助处理错误。
我该如何处理这个问题?我应该:
- 停止 url 更改的请求?
- 组件卸载时停止请求?
- 其他解决方案??
另一个选择是升级到 React 18 版本,该版本删除了此警告。它被删除的部分原因是它可能会产生误导:您的代码实际上不会造成内存泄漏。您获取一次数据,设置状态,然后返回。到那时,javascript 就可以清理闭包变量了。持久订阅可能会导致内存泄漏(例如,您忘记拆除的 Websocket),但您没有这样做。
如果您想在不更改反应版本的情况下消除警告,那么您应该添加一个清理功能来达到您的效果。您将保存一个变量,表明它已卸载,然后如果该变量已设置,则跳过设置状态。
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
const response = await apiGetCall("/departments");
if (cancelled) {
return;
}
const departmentsData = response.data.departments;
// Update the state with the fetched departmentData
setDepartmentData(departmentsData);
// Move the contents of pieChartOptions to the useState hook
setPieChartOptions({
// Department data
});
} catch (err) {
console.log(err);
}
};
fetchData();
return () => {
cancelled = true;
};
}, []);
此警告表明,即使发起调用的组件已卸载,某些异步代码/回调仍在运行。当组件卸载时,您应该清理这些“连接”,以免资源泄漏。
过去,建立了“isMounted”检查作为检查组件是否仍已安装并有条件地尝试状态更新的方法,但此后已不再推荐,并被认为是 React 反模式。 在这里,您应该在组件卸载时使用中止令牌来取消任何正在进行的网络请求。 (模式与旧的“isMounted”逻辑非常相似)。
有关详细信息,请参阅 AbortController 和 Axios
Cancellation 文档。
实现示例:
// axios utils
// (0) Update apiGetCall function to consume an options object arg
export const apiGetCall = (url, options) => axiosInstance.get(url, options);
// One of the function to get data from server and pass to chart
useEffect(() => {
// (1) Create an abort controller
const controller = new AbortController();
const fetchData = async () => {
try {
const { data } = await apiGetCall(
"/departments",
{
// (2) Pass controller signal in options to GET call
signal: controller.signal
}
);
const departmentsData = data.departments;
// Update the state with the fetched department data
setDepartmentData(departmentsData);
// Move the contents of pieChartOptions to the useState hook
setPieChartOptions({
// Department data
});
} catch (err) {
// (4) Aborted request Promise rejection, cancel error
console.log(err);
}
};
fetchData();
return () => {
// (3) Abort in-flight request on component unmount
controller.abort();
};
}, []);