ReactModal多次开闭后关闭时触发removeChild NotFoundError问题求助
ReactModal多次开闭后关闭时触发removeChild NotFoundError问题求助
这个问题我之前也遇到过,大概率是异步API请求和Modal状态更新不同步导致的React DOM操作冲突,以下是具体分析和解决方案:
问题根源
当你多次快速打开关闭Modal时,之前发起的API请求可能还处于pending状态,当请求完成后去更新组件状态(比如准备给Modal传数据),此时Modal已经被关闭卸载了,React尝试在已卸载的组件上进行DOM操作,就会抛出removeChild NotFoundError。
解决方案
1. 取消未完成的API请求(核心解决方法)
使用axios的CancelToken来取消在Modal关闭/组件卸载时未完成的请求,避免请求完成后去更新已卸载组件的状态:
首先在组件内定义一个CancelToken源的ref:
import axios from 'axios'; import { useRef, useEffect } from 'react'; // 组件内 const cancelTokenSource = useRef(null);
然后修改你的点击事件函数:
const handleClickManageGrades = async (e, studentId, classSubjectId) => { e.stopPropagation(); try { setIsProcessing(true); // 先取消之前未完成的请求,避免重复请求和状态冲突 if (cancelTokenSource.current) { cancelTokenSource.current.cancel('用户关闭了Modal,请求已取消'); } // 创建新的CancelToken源 cancelTokenSource.current = axios.CancelToken.source(); // 发起请求时带上CancelToken const res = await axios.get( `/api/student/performance/${classSubjectId}/${studentId}`, { cancelToken: cancelTokenSource.current.token } ); console.log(res); // 这里把请求到的数据存入状态,供Modal使用 // setStudentPerformance(res.data); openModal(); } catch (error) { // 取消请求的错误不需要提示toast if (!axios.isCancel(error)) { toast.error(error); } } finally { setIsProcessing(false); } };
接着在Modal关闭和组件卸载时取消请求:
const closeModal = () => { // 关闭Modal时取消未完成的请求 if (cancelTokenSource.current) { cancelTokenSource.current.cancel('Modal已关闭'); } setIsOpen(false); }; // 组件卸载时清理未完成的请求 useEffect(() => { return () => { if (cancelTokenSource.current) { cancelTokenSource.current.cancel('组件已卸载'); } }; }, []);
2. 调整请求和Modal打开的顺序(优化体验+避免冲突)
可以先打开Modal(展示加载状态),再发起请求,这样既提升用户体验,也能减少状态不同步的概率:
const handleClickManageGrades = async (e, studentId, classSubjectId) => { e.stopPropagation(); try { setIsProcessing(true); openModal(); // 先打开Modal,让用户看到加载状态 if (cancelTokenSource.current) { cancelTokenSource.current.cancel('请求已取消'); } cancelTokenSource.current = axios.CancelToken.source(); const res = await axios.get( `/api/student/performance/${classSubjectId}/${studentId}`, { cancelToken: cancelTokenSource.current.token } ); console.log(res); setStudentPerformance(res.data); // 更新Modal数据 } catch (error) { if (!axios.isCancel(error)) { toast.error(error); closeModal(); // 请求失败时关闭Modal } } finally { setIsProcessing(false); } };
3. 检查Modal组件的DOM操作冲突
如果使用的是第三方Modal组件(比如react-modal),确保:
- 只通过
isOpen状态控制Modal的显示隐藏,没有手动操作DOM的代码 - GridComponent的按钮事件没有被重复绑定,避免多次触发
handleClickManageGrades
总结
最核心的解决点就是取消未完成的异步请求,避免请求完成后去操作已卸载的Modal DOM节点。按照上面的方法修改后,应该就能解决这个removeChild错误了。
备注:内容来源于stack exchange,提问作者CodeEx
相关产品推荐
相关产品推荐

