setInterval + React 钩子导致组件内的多次更新 [英] setInterval + React hooks causing multiple updates within component

查看:28
本文介绍了setInterval + React 钩子导致组件内的多次更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个秒表 UI,以秒为单位显示时间.单击按钮,计时器将开始向上计数,并在再次单击时停止.用户应该能够再次启动它.

我遇到的问题是我可以让 setInterval 正常工作,但是一旦我包含了 setTime 钩子,组件就会更新以在 UI 中呈现时间,但是setInterval 实例被多次调用.这会导致奇怪的渲染行为.

const Timer = () =>{const [时间,设置时间] = useState(0)让计时器const startStopTimer = () =>{if (!timer) timer = setInterval(() => setTime(time++), 1000)别的 {清除间隔(定时器)计时器 = 空}}返回 (<div><p>时间:{time} 秒</p><按钮onClick={() =>{开始停止定时器()}>开始/停止</按钮>

)}

示例行为是:

  1. 用户点击开始/停止
  2. 计时器从 0 开始向上计数
  3. 用户点击开始/停止
  4. 计时器立即停止
  5. 用户点击开始/停止
  6. 计时器从停止的地方继续

解决方案

这是 React 钩子中陈旧闭包的经典示例,在调用 后,time 的 setInterval 值不会改变设置时间.更改您的代码:

setInterval(() => setTime(currentTime => currentTime + 1), 1000).

setTime 就像有类组件的 setState 也接受一个回调函数,它以当前值作为第一个参数

此外,timer 变量在您的代码中是无用的,因为在每次重新渲染时它都将是未定义的,您将无法访问 setInterval 的返回值,所以它会重新初始化setInterval.为了处理 useRef 的使用,您可以将 setInterval 的返回存储在 .current 中,在后续重新渲染后即可使用,因此无需更多重新初始化 setInterval 并且您也可以使用 clearInterval

解决方案:

const {useState, useRef} = React;const {render} = ReactDOM;const 定时器 = () =>{const [time, setTime] = useState(0);const timer = useRef(null);const startStopTimer = () =>{如果(!定时器.当前){timer.current = setInterval(() => setTime(currentTime => currentTime + 1), 1000);} 别的 {clearInterval(timer.current);timer.current = null;}};返回 (<div><p>时间:{time} 秒</p><按钮onClick={startStopTimer}>开始/停止

);};render(, document.getElementById("root"));

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></脚本><script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script><div id="root"></div>

I'm building a stopwatch UI that shows the time in seconds. With the click of a button, the timer will start counting upwards and stop when it is clicked again. User should be able to start it again.

The issue I'm having is that I can have setInterval working correctly but once I include setTime hook, the component updates to render the time in the UI but the setInterval instance is being called multiple times. This leads to odd rendering behavior.

const Timer = () => {
    const [time, setTime] = useState(0)
    let timer

    const startStopTimer = () => {
        if (!timer) timer = setInterval(() => setTime(time++), 1000)
        else {
           clearInterval(timer)
           timer = null
        }
    }

    return (
            <div>
               <p>Time: {time} seconds</p>
               <Button 
                   onClick={() => {
                      startStopTimer()
                   }
               > Start/Stop </Button>
            </div>
           )
}

Example behavior would be:

  1. User clicks Start/Stop
  2. Timer starts from 0 and counts upward
  3. User clicks Start/Stop
  4. Timer stops immediately
  5. User clicks Start/Stop
  6. Timer continues where it left off

解决方案

This is a classic example of stale closure in React hooks, inside your setInterval value of time is not changing after calling setTime. Change your code with:

setInterval(() => setTime(currentTime => currentTime + 1), 1000).

setTime just like the setState of classful components also accepts a callback function which has the current value as the first param

Also, the timer variable is useless in you code since on every re-render it will be undefined and you wont't have the access of return value of setInterval, so it will reinitialize the setInterval. To handle that use useRef, you can store the return of setInterval in .current, which will be available to you after subsequent re renders so no more re-init of setInterval and you can also use clearInterval

Solution:

const {useState, useRef} = React;
const {render} = ReactDOM;

const Timer = () => {
  const [time, setTime] = useState(0);
  const timer = useRef(null);
  const startStopTimer = () => {
    if (!timer.current) {
      timer.current = setInterval(() => setTime(currentTime => currentTime + 1), 1000);
    } else {
      clearInterval(timer.current);
      timer.current = null;
    }
  };

  return (
    <div>
      <p>Time: {time} seconds</p>
      <button
        onClick={startStopTimer}
      >
        Start/Stop
      </button>
    </div>
  );
};

render(<Timer />, document.getElementById("root"));

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

这篇关于setInterval + React 钩子导致组件内的多次更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆