You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 07:13:04