这个错误非常令人困惑:
https://twitter.com/kentcdodds/status/1102659818660102145
我想这是因为fn
按计划setInterval(fn, 0)
在前面跳跃[running]
造成的影响清理setRunning(false)
。 所以间隔仍是起火,覆盖setLapse(0)
其在活动期间所发生setLapse(someValue)
。
这让我想起了https://github.com/facebook/react/issues/14750#issuecomment -460409609 中描述的问题,或者至少是其中的一部分:
事实上,即使对于常规的 React 击键(和其他“离散”事件),这个问题也存在。 解决方案是在我们获得离散事件之前清除被动效果。
但在这里,好像因为效果翻转的点击setState
也应该刷新被动效果? 好像没有。 (这将违背拖延他们的目的。)
所以这是按设计工作的,当时间很重要时,修复只是useLayoutEffect
? 还是 rAF 解决方案?
问题是计时器本身不是一个离散事件。 离散事件仅在与其他离散事件相关时得到保证。 我不认为这就是你想要的。
停止计时器是一个异步操作,因为所有设置的状态都是异步的。 所以其他东西可以在它被冲洗之前进入。
在这种情况下 useLayoutEffect 实际上并没有完全解决问题。 当然不是在并发模式下,但这在同步模式下也是粗略的。 如果它不是一个计时器,而是一个焦点事件,那么它可以在刷新之前在批处理中触发,这将具有相同的问题。
setRunning(false); // I would like to add a stop of this timer to the queue to be performed later
setLapse(0); // I would like to add an operation to set lapse to zero later
// lots of random stuff that can happen before the batch flushes
// This might also queue an operation to set lapse to something else
// actual rendering
// If concurrent mode, lots of other random stuff that can happen while rendering
// This might also queue an operation to set lapse to something else
// Actually do all that work in order
一切都与将事物添加到队列中的顺序有关。
一种方法是仅在计时器实际停止时(即在效果中)才调度重置。 如果你想让它在同一帧中始终显示零,那么你可以在运行为false时始终显示零。
然而,与往常一样,这更好地建模为减速器。 当 reducer 认为它处于停止状态时,它可以很容易地拒绝更新失效,并执行逻辑来实际重置它。
最有用的评论
一种方法是仅在计时器实际停止时(即在效果中)才调度重置。 如果你想让它在同一帧中始终显示零,那么你可以在运行为false时始终显示零。
然而,与往常一样,这更好地建模为减速器。 当 reducer 认为它处于停止状态时,它可以很容易地拒绝更新失效,并执行逻辑来实际重置它。