React中使用setInterval+useState报错:Failed to execute 'removeChild' on 'Node'
问题原因及修复方案
你的代码出现「Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node」错误,核心原因有两个:
- useEffect未设置依赖数组:组件每次渲染(包括
seconds更新后)都会新建一个定时器,多个定时器同时运行,频繁修改状态导致DOM更新逻辑混乱,触发节点移除错误。 - 闭包陷阱:定时器回调里的
seconds是创建定时器时捕获的旧值,比如初始值30,即便setSeconds更新了状态,后续定时器依然用旧值计算,导致状态更新异常,进一步加剧DOM冲突。
修复方案一:添加依赖数组+函数式更新
给useEffect指定依赖[seconds],确保seconds变化时清理旧定时器并创建新的;同时用函数式更新setSeconds,确保每次拿到最新状态值:
const [seconds, setSeconds] = useState(30); useEffect(() => { // 倒计时结束就不再创建定时器 if (seconds <= 0) return; const myInterval = setInterval(() => { setSeconds(prevSeconds => prevSeconds - 1); }, 1000); return () => clearInterval(myInterval); }, [seconds]);
修复方案二:单次挂载定时器+useRef保存ID
如果不想每次seconds变化都重建定时器,可以用useRef保存定时器ID,仅在组件挂载时创建一次定时器,通过函数式更新处理状态:
import { useState, useEffect, useRef } from 'react'; const [seconds, setSeconds] = useState(30); const intervalRef = useRef(null); useEffect(() => { intervalRef.current = setInterval(() => { setSeconds(prev => { if (prev <= 0) { clearInterval(intervalRef.current); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(intervalRef.current); }, []);
这两种方案都能避免多个定时器同时运行,解决闭包导致的状态更新异常,从而消除DOM操作错误。
内容的提问来源于stack exchange,提问作者Nick Jonas
相关产品推荐
相关产品推荐

