React中useEffect内setTimeout无限运行导致Toast组件无限重渲染如何解决
问题根源
你遇到的无限重渲染是由第二个useEffect的逻辑和状态更新逻辑共同导致的:
- 你把整个
showState对象作为该useEffect的依赖项,只要showState引用变化就会触发effect执行 - 定时器执行
setShowState时传入的是新的对象字面量,哪怕所有属性值和旧状态完全一致,React也会判定为状态更新,触发组件重渲染 - 重渲染后
showState引用变化,再次触发该useEffect,开启新的定时器,2秒后又执行状态更新,形成死循环
解决方案
调整第二个useEffect的逻辑,只有当Toast处于显示状态时才启动隐藏定时器,同时减少不必要的状态更新:
- 给effect逻辑加显示状态判断,隐藏状态下不启动定时器
- 依赖项只需要
showState.top即可,不需要监听整个对象 - 状态更新时增加前置判断,已经是隐藏状态就不执行更新,避免无意义的重渲染
修改后的代码如下:
// hide notification after 2 seconds useEffect(() => { // 非显示状态直接返回,不启动定时器 if (showState.top <= 0) return const timerId = setTimeout(() => { setShowState(prevState => { // 已经是隐藏状态就返回原状态,不触发更新 if (prevState.top === -100) return prevState return { top: -100, msg: '', bgColor: `#ffffff00`, } }) }, 2000) return () => { clearTimeout(timerId) } }, [showState.top])
额外优化建议
如果你希望Toast隐藏后,新的通知到来时能正常触发显示逻辑,可以检查redux返回的create_notification.success_info是否每次返回新的对象引用,若message和code无变化可缓存该对象,避免触发不必要的第一个useEffect执行。
内容的提问来源于stack exchange,提问作者osmancakirio
相关产品推荐
相关产品推荐

