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

为何setState在动画帧中被同步调用?React状态更新异常

动画帧内调用setState引发的同步执行与Effect异常问题

我在动画帧(animation frame)内调用setState,原本预期它会像常规情况一样进入队列等待执行,但实际它却立即同步执行。更严重的是,若存在待处理的effect,这些effect也会被立即解析,导致effect的清理函数在动画帧执行过程中就被调用,使得我请求的下一帧无法被清理。观察示例中的计时器,会发现它的速度在不断加快。

示例代码

JavaScript

let handling = false
let accum = 0

function MyComponent() {
    const [state, setState] = React.useState(0)
    
    React.useEffect(() => {
        let frame
        let time = undefined
        function handle(t) {
          handling = true
          frame = undefined
          if (time != null) {
            // 这里只是为了演示,避免React性能限制影响效果
            accum += t - time
            setState(Math.floor(accum / 1000))
          }
          time = t
          frame = window.requestAnimationFrame(handle)
          handling = false
        }
        if (handling) console.log('oh no!')
        frame = window.requestAnimationFrame(handle)
        return () => window.cancelAnimationFrame(frame)
    })

    React.useEffect(() => {
      window.setInterval(() => {
        setState(x => x + 0.0001)
      }, 111)
    }, [])
    
    return <div>{Math.round(state)}</div>
}

ReactDOM.render(<MyComponent/>, document.querySelector('.root'))

HTML

<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div class="root"></div>

临时规避方案

我知道可以修改为如下代码来避免在不应该的时候请求新帧,但这类繁琐的代码理应可以规避:

let frame
let time = undefined
let effectIsOver = false
function handle(t) {
  handling = true
  frame = undefined
  if (time != null) {
    setState(s => s + t - time)
  }
  time = t
  if (!effectIsOver) frame = window.requestAnimationFrame(handle)
  handling = false
}
frame = window.requestAnimationFrame(handle)
return () => {
  window.cancelAnimationFrame(frame)
  effectIsOver = true
}

内容的提问来源于stack exchange,提问作者riv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:12:24