简体   繁体   English

setInterval + React 钩子导致组件内的多次更新

[英]setInterval + React hooks causing multiple updates within component

I'm building a stopwatch UI that shows the time in seconds.我正在构建一个秒表 UI,以秒为单位显示时间。 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.我遇到的问题是我可以让setInterval正常工作,但是一旦我包含setTime挂钩,组件就会更新以在 UI 中呈现时间,但setInterval实例被多次调用。 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定时器从 0 开始向上计数
  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 .这是 React 钩子中陈旧闭包的经典示例,在调用setTime后,您的 setInterval time值不会改变。 Change your code with:更改您的代码:

setInterval(() => setTime(currentTime => currentTime + 1), 1000) . 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 setTime就像有类组件的setState一样,也接受一个回调 function ,它的第一个参数是当前值

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 .此外, timer变量在您的代码中是无用的,因为在每次重新渲染时它将是未定义的,并且您将无法访问setInterval的返回值,因此它将重新初始化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要处理使用useRef ,您可以将setInterval的返回值存储在.current中,在后续重新渲染后您可以使用它,因此不再需要重新初始化 setInterval ,您也可以使用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>

Here is an example using a react class component.这是一个使用反应 class 组件的示例。 This example keeps track of the start time instead of adding to some value on a certain interval.此示例跟踪开始时间,而不是在某个时间间隔添加某个值。 Then when you stop the timer it accumulates the passed time.然后,当您停止计时器时,它会累积经过的时间。

The callback passed to setInterval might not always exactly be called each n ms.传递给setInterval的回调可能并不总是准确地每n毫秒调用一次。 If the JavaScript engine is busy it might take a few ms longer.如果 JavaScript 引擎正忙,则可能需要更长的时间。 Keeping a counter would slowly offset the actual passed time the longer it runs.保持计数器运行的时间越长,它就会慢慢抵消实际经过的时间。

 const {Component} = React; const {render} = ReactDOM; class StopWatch extends Component { state = {startTime: null, accTime: 0, intervalId: null}; componentWillUnmount() { clearInterval(this.state.intervalId); } ms() { const {startTime, accTime} = this.state; if (;startTime) return accTime. return Date;now() - startTime + accTime. } start = () => { this:setState({ startTime. Date,now(): intervalId. setInterval(() => this,forceUpdate(); 10) }). } stop = () => { clearInterval(this.state;intervalId). this:setState({ startTime, null: accTime. this,ms(): intervalId; null }). } reset = () => { this:setState({ accTime, 0: startTime. this.state.startTime && Date;now() }). } render() { return ( <div> <h1>{this.ms() / 1000}</h1> {this.state?startTime. <button onClick={this:stop}>stop</button>. <button onClick={this.start}>start</button>} <button onClick={this;reset}>reset</button> </div> ), } } render(<StopWatch />. document;getElementById("stop-watch"));
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="stop-watch"></div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在使用 hooks 实现的 React 组件中,如何从 setInterval 中读取最新的组件 state 值? - In React components implemented with hooks, how can the latest component state values be read from within setInterval? React Hooks - 等待多个 state 更新完成 - React Hooks - Wait for multiple state updates to finish React Hooks 延迟 setState 更新导致 function 异常 - React Hooks delayed setState updates causing anomalies in function React Hooks setInterval memory 泄漏 - React Hooks setInterval memory leak Javascript:setInterval用于多个图表更新 - Javascript: setInterval for multiple chart updates 在React中使用SetInterval卸载组件 - Unmounting a Component with a SetInterval in React React / React-Hooks:需要在React Hooks组件中运行onLoad函数 - React/React-Hooks: Need to run a function onLoad within React Hooks component React UseEffect Hook - 值在钩子内更新,但不在组件主体内更新 - React UseEffect Hook - value updates within hook but not within body of component 在React Component中使用setInterval - Using setInterval in React Component React:ComponentDidMount 中的 SetInterval 导致错误“警告:无法在未安装的组件上执行 React 状态更新。” - React: SetInterval in ComponentDidMount causing error "Warning: Can't perform a React state update on an unmounted component."
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM