【问题标题】:React Countdown Timer unable to make a stop functionReact Countdown Timer 无法停止功能
【发布时间】:2020-05-04 18:36:58
【问题描述】:

我几乎是 React 的新手。在这个应用程序中,我正在使用 Hooks! 我制作了一个倒数计时器,它会在登录后几秒钟内显示出来。我无法让它在单击按钮时停止。我需要一些建议,因为过去两天我一直在为此苦苦挣扎。 到目前为止,这是我的代码:(请帮助)

function Admin() {
    const [isTimerOpen, setTimmer] = useState(false);
    let history = useHistory();

    // SET BY THE ADMIN
    var minutesToCountDown = 0.9;
    // TRANSFORM INTO SECONDS
    var transformMinutesToSeconds = minutesToCountDown * 60
    // KEEP A STATE
    const [counterValue, setCounterValue] = useState(0);
    const [isTimmerStoped, setStopTimer] = useState(false);

    // FUNCTION TO HAPPEN EVERY 1 SECOND
    function timeIt() {
        if (isTimmerStoped === false) {
            transformMinutesToSeconds--
            setCounterValue(transformMinutesToSeconds)
            console.log("Timer is on: ", transformMinutesToSeconds)

            if (transformMinutesToSeconds === 0) {
                clearInterval(interval)
                setStopTimer(true)
            }
        } else {
            setStopTimer(true)
            clearInterval(interval)
        }
    }

    // STARTS THE COUNTDOWN
    var interval;
    const startCountdown = () => {
        interval = setInterval(timeIt, 1000)
    }

    const stopCountdown = () => {
        console.log("Stop Timer")
        setStopTimer(true);
        setCounterValue(0);
        setTimmer(false);
    }

    // ADD 0 IN FRONT ON THE TIME REMAINING
    const addLeadingZeros = value => {
        value = String(value);
        while (value.length < 2) {
            value = `0${value}`;
        }
        return value;
    };

    // CONVERT SECONDS INTO TIME REMAINING
    function convertSeconds(seconds) {
        var min = Math.floor(seconds / 60);
        var sec = seconds % 60;
        return addLeadingZeros(min) + ':' + addLeadingZeros(sec)
    }

    const logOutUser = () => {
        logout();
        return history.push(mainRoute)
    }

    function setTimer() {
        const timer = setTimeout(() => {
            setTimmer(true)
            console.log('This will run after 3 seconds!')
            startCountdown()

        }, sessionTimeout);
        return () => clearTimeout(timer);
    }

    useEffect(() => {
        if (isTimmerStoped === false) {
            console.log('Effect Starting', isTimmerStoped)
            setTimer()
        } else {
            console.log('Effect Stopping', isTimmerStoped)
            stopCountdown()
        }

      }, [isTimmerStoped, setStopTimer, minutesToCountDown]);



    return <React.Fragment>
            <CssBaseline />
            <Container disableGutters maxWidth={false}>
                <NavigationBar handleSignOut={logOutUser}/>
                <TimerContent 
                    timeRemaining={convertSeconds(counterValue)}
                    isTimerAlertOpen={isTimerOpen}
                    extendSessionBtn={stopCountdown}
                    logoutBtn={logOutUser}  
                    clickOutsideButton={stopCountdown}/>
            </Container>
    </React.Fragment>  
}

export default Admin;

【问题讨论】:

    标签: javascript reactjs react-native react-hooks


    【解决方案1】:

    你应该做两件事。

    1. interval 变量设为引用。这样,它的价值在它导入的每个地方都是独一无二的。注意:只在组件上方创建一个变量是个坏主意,因为该变量将在导入 Admin 组件的每个组件之间共享,这会导致错误。

    错误

    let interval;
    
    function Admin() {
      //... code here
    
      // STARTS THE COUNTDOWN
      // var interval; Remove from here
      const startCountdown = () => {
          interval = setInterval(timeIt, 1000)
      }
    
      //... code here
    }
    
    export default Admin;
    

    function Admin() {
      const interval = React.useRef(); 
      //... code here
    
      // STARTS THE COUNTDOWN
      // var interval; Remove from here
      const startCountdown = () => {
          interval.current = setInterval(timeIt, 1000)
      }
    
      //... code here
    }
    
    export default Admin;
    
    1. clearInterval 添加到您的stopCountdown 函数中。您可以删除 timeIt 函数中的 clearInterval 并将其移动到 stopCountdown 中。请看看我做的这个codepen演示
    const stopCountdown = () => {
      console.log("Stop Timer")
      setStopTimer(true);
      setCounterValue(0);
      setTimmer(false);
      clearInterval(interval.current) // Clear the interval here
    }
    

    【讨论】:

    • 干杯,效果很好!我之前在 stopCountdown 函数中使用了清除间隔,但我从未想过将间隔取到函数之外。谢谢!
    • 一旦多个Admin 组件被渲染,这将失败,因为interval 将被所有Admin 组件共享(覆盖)。
    • @EmileBergeron 这不是真的。如果Admin 组件与您使用它的文件位于不同的文件中,则每个Admin 实例的间隔值将不同。我做了一个演示codesandbox.io/s/nifty-violet-po3zu?file=/src/Admin.js
    • 您的代码和框不起作用,停止按钮甚至没有连接到任何东西,即使它是,它也不起作用,因为状态被闭包(setInterval 回调)捕获并且永远不会获取停止所需的更新状态值...
    • 是的,你是对的,我已经更新了沙箱中的代码并更新了使用 useRef 的答案。感谢您指出这一点
    【解决方案2】:

    您不应在函数变量中维护 setInterval 返回的 intervalId,因为它将在重新渲染时重置,并且您将丢失实际的计时器 ID。您必须改为使用 useRef 挂钩来存储 timerId

       const interval = useRef(null);
        ...
       function timeIt() {
            if (isTimmerStoped === false) {
                transformMinutesToSeconds--
                setCounterValue(transformMinutesToSeconds)
                console.log("Timer is on: ", transformMinutesToSeconds)
    
                if (transformMinutesToSeconds === 0) {
                    clearInterval(interval.current)
                    setStopTimer(true)
                }
            } else {
                setStopTimer(true)
                clearInterval(interval.current)
            }
        }
        ...
       // STARTS THE COUNTDOWN
        const startCountdown = () => {
            interval.current = setInterval(timeIt, 1000)
        }
    

    【讨论】:

    • 谢谢,我很欣赏这个答案,你是对的,间隔不应该保留在函数内......现在意识到了,但是由于某种原因 useRef 对我不起作用。另一个解决方案有效。还是谢谢你,非常感谢!
    • 将间隔值移到组件之外意味着您只能使用组件的 1 个实例,这对您来说现在可能就足够了,但这不是正确的方法。正确的方法是使用 useRef。也许你没有使用导致问题的`interval.current`
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多