使用函数式 Hooks 的 React Timer | 定时器没有停止

IT技术 javascript reactjs react-hooks
2021-05-22 04:22:13

我想创建60年代的时候使用挂钩reactuseStateuseEffects这是我在做什么

import '../assets/css/timer.css'
import { useState, useEffect } from 'react'

const Timer = () =>{

    const [ time, setTime ] = useState(0);

    useEffect(()=>{
        if(time!==60){
            setInterval(()=>{
                    setTime(prevTime => prevTime+1) ;
            }, 1000);
        }
        
    
    }, [])


    return(
        <>

            <div className="circular">
                <div className="inner"></div>
                <div className="outer"></div>
                <div className="numb">
                    {time}  // Place where i am displaying time
                </div>
                <div className="circle">
                    <div className="dot">
                    <span></span>
                    </div>
                    <div className="bar left">
                    <div className="progress"></div>
                    </div>
                    <div className="bar right">
                    <div className="progress"></div>
                    </div>
                </div>
            </div>
        </>
    )
}

export default Timer

定时器没有停止。它永远持续下去。我也试过这个

    useEffect(()=>{

        setInterval(()=>{
            if(time!==60)
                setTime(prevTime => prevTime+1) ;
        }, 1000);
    
    }, [])

可以请一些人解释哪里出了问题。

3个回答

useEffect(..., [])只会运行一次,所以time它里面永远不会更新。所以需要prevTimesetTime函数内部进行检查,如果不是60则只自增。如果,则清空区间,然后在useEffect的清理中清空区间:

useEffect(()=>{
    const i = setInterval(() => {
        setTime(prevTime => {
            if (prevTime !== 60) return prevTime+1;
            clearInterval(i);
            return prevTime;
        });
    }, 1000);
    return () => clearInterval(i);
}, [])

您即将在第一次尝试中使用它,但存在一些问题。

主要问题是您传递了一个空的依赖数组,这意味着它只会在第一次渲染时运行,而不会在连续渲染时更新。其次,您不提供返回或“清理”,这意味着该间隔永远不会被清除。

  useEffect(() => {
    if (time < 60) {
      const timer = setInterval(() => {
        setTime(prevTime => prevTime + 1);
      }, 1000);

      return () => clearInterval(timer);
    }
  }, [time])

在这里,我们传入time依赖项数组并有条件地设置间隔 iftime小于您的结束时间,在您的情况下为 60,但我将其缩短为 5,以便您可以看到它停止。我们还传递了一个return回调,它将在每个渲染周期结束时清除间隔。

每次 setInterval 更新time状态时进行此设置,useEffect 将在前一次渲染结束时清除间隔,然后在当前渲染中重新运行,如果时间小于限制,则再次设置间隔。

以这种方式使用 React 渲染周期的优点,而不是清除间隔回调中的间隔,是它可以让您对超时进行精细控制 - 允许您轻松添加进一步检查或根据其他状态值延长/缩短时间.

const { useState, useEffect } = React;

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

  useEffect(() => {
    if (time < 5) {
      const timer = setInterval(() => {
        setTime(prevTime => prevTime + 1);
      }, 1000);

      return () => clearInterval(timer);
    }
  }, [time])

  return (
    <div className="circular">
      <div className="numb">
        {time}
      </div>
    </div>
  )
}

ReactDOM.render(
  <Timer />,
  document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="root"></div>

您必须在 Unmount 状态下清除 setTimeout

对于功能组件

// Funtional component
useEffect(()=>{
  const i = setInterval(() => {
    setTime(prevTime => {
        if (prevTime !== 60) return prevTime+1;
        clearInterval(i);
        return prevTime;
    });
  }, 1000);
  return () => clearInterval(i);
}, [])

对于类组件

// Class component
componentWillUnmount() {
  this.clearInterval()
}