React调用API更新state时报无法对已卸载组件执行状态更新错误如何解决
问题原因
你遇到的警告触发逻辑是:页面组件已经卸载(比如用户跳转到其他页面)时,之前发起的异步接口请求还在执行,请求返回后仍然尝试给已卸载的组件更新状态,存在内存泄漏风险。
解决方案
1. 先修正语法错误
你现有代码中第一个useEffect缺少闭合括号,先补全:
useEffect(() => { fetchUserData(auth.apiToken).then((data) => { console.log(data); setUser(data); }) // 原代码漏了这行的闭合 }, [auth.apiToken]);
2. 给所有异步请求的useEffect加挂载状态判断
在每个发请求的useEffect里加一个标记变量,跟踪组件是否还挂载,只有组件未卸载时才更新状态:
// 获取用户信息的useEffect修改后 useEffect(() => { let isMounted = true; fetchUserData(auth.apiToken).then((data) => { if (isMounted) { console.log(data); setUser(data); } }); // 组件卸载时更新标记 return () => { isMounted = false; }; }, [auth.apiToken]); // 获取项目列表的useEffect修改后 useEffect(() => { let isMounted = true; fetchUserProjects(auth.apiToken).then((data) => { if (isMounted) { console.log(data); setIdKitData(data); } }); return () => { isMounted = false; }; }, [auth.apiToken]); // 获取Map数据的useEffect修改后 // 注意:这里要把idKitData加入依赖数组,还要加空值判断避免初始状态报错 useEffect(() => { let isMounted = true; // 只有idKitData有值且存在projects数组时才发请求 if (idKitData?.projects?.length) { fetchMapById(auth.apiToken, idKitData.projects[1].lab.id).then((data) => { if (isMounted) { console.log(data); setKitData(data); } }); } return () => { isMounted = false; }; }, [auth.apiToken, idKitData]); // 获取点位数据的useEffect修改后 useEffect(() => { let isMounted = true; fetchEncounterByMaps(auth.apiToken).then((data) => { if (isMounted) { console.log(data); setMapsData(data); } }); return () => { isMounted = false; }; }, [auth.apiToken]);
3. 可选:更严谨的方案,主动取消未完成请求
如果你的api是基于fetch封装的,可以用AbortController在组件卸载时直接取消未完成的请求,从根源避免后续逻辑执行:
首先调整请求函数,接收signal参数:
export const fetchMapById = async (token, projectId, signal) => { const {data} = await api(`/projects/v1/project/${projectId}`, { method: "GET", headers: { Authorization: `Bearer ${token}`, }, signal // 传入取消信号 }) return data; };
然后修改对应useEffect:
useEffect(() => { const controller = new AbortController(); if (idKitData?.projects?.length) { fetchMapById(auth.apiToken, idKitData.projects[1].lab.id, controller.signal) .then(data => setKitData(data)) .catch(err => { // 忽略请求取消的报错 if (err.name !== 'AbortError') console.error(err); }) } // 组件卸载时取消请求 return () => controller.abort(); }, [auth.apiToken, idKitData])
4. 额外逻辑优化
因为fetchMapById依赖fetchUserProjects的返回结果,你可以把两个请求合并到同一个useEffect中,减少不必要的重渲染:
useEffect(() => { let isMounted = true; const fetchAllData = async () => { // 先拿项目列表 const projectData = await fetchUserProjects(auth.apiToken); if (isMounted) setIdKitData(projectData); // 再拿对应map数据 if (projectData?.projects?.length) { const mapData = await fetchMapById(auth.apiToken, projectData.projects[1].lab.id); if (isMounted) setKitData(mapData); } } fetchAllData(); return () => { isMounted = false; } }, [auth.apiToken])
修改后即可解决警告,同时保证接口数据正常返回供后续逻辑使用。
内容的提问来源于stack exchange,提问作者moid tera3
相关产品推荐
相关产品推荐

