如何清除 React 组件中使用的计时器? [英] How do I clear a timer using in my React component?

查看:106
本文介绍了如何清除 React 组件中使用的计时器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试从控件重置计数器时,我无法弄清楚为什么我的计数器不会重置.我怀疑我在如何从控件中操纵状态时犯了某种新手(常见且不尴尬)的错误.

I'm having trouble figuring out why my counter won't reset when I attempt to reset it from a control. I suspect that that I'm making some kind of novice (common and not embarrassing) mistake in how I manipulate state from within my controls.

例如,如果我多次计时Faster",然后单击Normal"计数继续加速:显然更快的计时器没有被 startTimer 的调用清除.只有随后单击重置"或停止"然后单击开始",更快的计时器才会出现清除.但我不明白为什么会这样:所有路径都以相同的方式使用 clearInterval.

For example, if I clock "Faster" several times and then click "Normal" counting continues at an accelerated pace: apparently the faster timer has not been cleared by the invocation of startTimer. Only by subsequently clicking "Reset", or "Stop" followed by "Start" does the faster timer appear to clear. But I'm mystified by why this should be the case: all paths use clearInterval in the same way.

我怀疑我没有掌握有关如何在组件中操纵状态的一般知识;或者如何从组件状态正确访问计时器.

I suspect that I'm not grasping something general about how state is manipulated in a component; or perhaps how to correctly access a timer from component state.

为什么我的计时器不能按预期清除?

Why can't I get my timer to clear as expected?

WobblyCounter.tsx:

import React, { useState } from 'react'
import { View, Button, Text } from 'native-base'
import { useDispatch, useSelector } from 'react-redux'

const WobblyCounter = () => {

    const [ timerID, setTimerID ] = useState(0)
    const [ isRunning, updateIsRunning ] = useState(false)
    const [ interval, updateInterval ] = useState(1000)

    const count = useSelector((state) => state.count)
    const dispatch = useDispatch()

    const startTimer = (): void => {
        clearInterval(timerID)
        setTimerID(setInterval(() => { dispatch( {type: "INCREMENT", step: 1} ) }, interval))
        updateIsRunning(true)
    }

    const stopTimer = (): void => {
        clearInterval(timerID)
        updateIsRunning(false)
    }

    return (
        <View style={ {paddingTop:50} }>
            <Button
                onPress={ (): void => { dispatch( {type: "RESET"} ); startTimer() } }>
                <Text>Reset</Text>
            </Button>
            <View style={ {flexDirection: "row"} }>
                <Button small bordered dark disabled={ interval <= 250 }
                    onPress={ (): void => { updateInterval(Math.max(interval - 250, 250)); startTimer() } }>
                    <Text>Faster</Text>
                </Button>
                <Button small bordered dark disabled={ interval == 1000 }
                    onPress={ (): void => { updateInterval(1000); startTimer() } }>
                    <Text>Normal</Text>
                </Button>
                <Button small bordered dark
                    onPress={ (): void => { updateInterval(interval + 250); startTimer() } }>
                    <Text>Slower</Text>
                </Button>
            </View>
            <Button small style={ Object.assign( {}, {backgroundColor: isRunning ? "red" : "green"} ) }
                onPress={ (): void => { isRunning ? stopTimer() : startTimer() } }>
                <Text>{isRunning ? "Stop" : "Start"}</Text>
            </Button>
            <Text>
                Debug{"\n"}count = {count}{"\n"}interval = {interval}{"\n"}timerID = {timerID}
            </Text>
        </View>
    )

}

export default WobblyCounter

推荐答案

这里的主要问题是闭包 startTimer 使用的是旧状态值:

The main problem here is that the closure startTimer is using old state value :

  1. 在第一次渲染 timerID=0 和 interval=1000 时,会使用这些值创建 startTimer.
  2. 当您点击Faster"时,updateInterval 被调用,并且间隔状态更改为 750 但组件尚未呈现,因此 startTimer 被调用旧值区间=1000.
  3. 组件重新渲染 timerID=1 和 interval=750,startTimer 使用这些值重新创建.
  4. 当你点击Normal"时,updateInterval 被调用并且间隔状态改变为 1000 但组件还没有被渲染,startTimer 被调用旧值区间=750.这就是计数器仍在快速运行的原因.
  1. on first render timerID=0 and interval=1000, startTimer is created with these values.
  2. when you click on "Faster", updateInterval is called and the interval state is changed to 750 but the component isn't rendered yet, and so startTimer is called with the old value interval=1000.
  3. The component re-render timerID=1 and interval=750, startTimer is re-created with these values.
  4. when you click on "Normal", updateInterval is called and the interval state is changed to 1000 but the component isn't rendered yet, startTimer is called with the old value interval=750. That is why the counter is still running fast.

解决此问题的一种方法是使用自定义钩子 useInterval 此处由 Dan Abramov 提出,并且仅在点击按钮时更新相关状态(间隔、isRunning).

One way to fix this problem is to use a custom hook useInterval proposed here by Dan Abramov, and only update the relevant state (interval, isRunning) when the buttons are clicked.

  useInterval(
     () => {
         dispatch({ type: "INCREMENT", step: 1 });
     },
     isRunning ? interval : null
  );

你可以在这里找到完整的代码(我删除了 react-本机)

You can find the completed code here (I removed react-native)

这篇关于如何清除 React 组件中使用的计时器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆