gpt4 book ai didi

javascript - "official"useInterval 示例中的潜在错误

转载 作者:行者123 更新时间:2023-12-04 04:27:26 26 4
gpt4 key购买 nike

使用间隔useInterval来自 this blog post by Dan Abramov (2019) :

function useInterval(callback, delay) {
const savedCallback = useRef();

// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);

// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
一个潜在的错误
可以在提交阶段和 useEffect 之间调用间隔回调。调用,导致调用旧的(因此不是最新的)回调。换句话说,这可能是执行顺序:
  • 渲染阶段 - callback 的新值.
  • 提交阶段 - 提交给 DOM 的状态。
  • 使用布局效果
  • 间隔回调 - 使用 savedCallback.current() , 不同于 callback .
  • 使用效果 - savedCallback.current = callback;

  • React 的生命周期
    为了进一步说明这一点,这里是 a diagram showing React's Life Cycle with hooks :
    React lifecycle for functional components
    虚线表示异步流(事件循环已释放),您可以在这些点进行间隔回调调用。
    但是请注意, Render 之间的虚线和 React updates DOM (提交阶段)很可能是 a mistake .如 this codesandbox演示,您只能在 useLayoutEffect 之后调用间隔回调。或 useEffect (但不是在渲染阶段之后)。
    所以你可以在 3 个地方设置回调:
  • 渲染 - 不正确,因为状态更改尚未提交到 DOM。
  • useLayoutEffect - 正确,因为状态更改已提交给 DOM。
  • useEffect - 不正确,因为旧的间隔回调可能会在此之前触发(在布局效果之后)。

  • 演示
    此错误已在 codesandebox 中演示。 .重现:
  • 将鼠标移到灰色 div 上 - 这将导致新的渲染 callback引用。
  • 通常,您会看到在少于 2000 次鼠标移动时引发的错误。
  • 间隔设置为 50 毫秒,因此您需要一点运气才能让它在渲染和效果阶段之间正确触发。

  • A screenshot of a code sandbox showing an exception thrown when the effect callback is not up to date
    用例
    演示显示当前回调值可能与 useEffect中的不同好的,但真正的问题是 其中哪一个是“正确”的 ?
    考虑这段代码:
    const [size, setSize] = React.useState();

    const onInterval = () => {
    console.log(size)
    }

    useInterval(onInterval, 100);
    如果 onInterval在提交阶段之后但在 useEffect 之前调用,它将打印错误的值。

    最佳答案

    尽管我理解讨论,但这对我来说似乎不是一个错误。
    上面的答案建议在渲染期间更新 ref 将是一个副作用,should be avoided because it will cause problems .

    The demo shows that the current callback value may differ from that in useEffect alright, but the real question is which one of them is the "correct" one?


    我相信“正确”的是已经 promise 的。出于一个原因,提交的效果是唯一可以保证以后有清理阶段的效果。 (这个问题中的间隔不需要清理效果,但其他东西可能。)
    在这种情况下,另一个更令人信服的原因可能是 React 可能会预渲染事物(或者以较低的优先级,或者因为它们“离屏”并且还不可见,或者在动画 API 的 future b'c 中)。像这样的预渲染工作不应该修改 ref,因为修改是任意的。 (考虑一个 future 的动画 API,它预渲染多个可能的 future 视觉状态,以更快地响应用户交互。你不希望最后渲染的那个只是改变你当前可见/提交的引用看法。)

    编辑 1 这个讨论似乎主要是在指出,当 JavaScript 不同步(阻塞)时,当它在渲染之间产生时,有可能在两者之间发生其他事情(比如之前安排的计时器/间隔)。这是真的,但如果在渲染期间发生这种情况(在“提交”更新之前),我认为这不是一个错误。
    如果主要担心回调可能在 UI 提交后执行并且与屏幕上的内容不匹配,那么您可能需要考虑 useLayoutEffect 反而。这种效果类型在提交阶段被调用,在 React 修改 DOM 之后但在 React 返回给浏览器之前(也就是没有间隔或计时器可以在其间运行)。

    编辑 2 我相信 Dan 最初建议为此使用 ref 和效果(而不仅仅是效果)的原因是因为对回调的更新不会重置间隔。 (如果每次回调改变时调用 clearIntervalsetInterval,整个时间都会中断。)

    关于javascript - "official"useInterval 示例中的潜在错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68407187/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com