【问题标题】:How to start and stop timer display in ReactJS如何在 ReactJS 中启动和停止计时器显示
【发布时间】:2021-10-20 22:48:44
【问题描述】:

我正在尝试在 ReactJS 中创建番茄钟。我无法让计时器停止倒计时。

PomView.js

const PomView = () => {
    const [timer, setTimer] = useState(1500)    // 25 minutes
    const [start, setStart] = useState(false)
    var firstStart = useRef(true)
    var tick;

    useEffect( () => {
        if (firstStart.current) {
            console.log("first render, don't run useEffect for timer")
            firstStart.current = !firstStart.current
            return
        }

        console.log("subsequent renders")
        console.log(start)
        if (start) {
            tick = setInterval(() => {
            setTimer(timer => {
                timer = timer - 1
                console.log(timer)
                return timer       
                }
            )
        }, 1000)
    } else {
        console.log("clear interval")
        clearInterval(tick);
    }
    }, [start])

    const toggleStart = () => {
        setStart(!start)
    }


    const dispSecondsAsMins = (seconds) => {
        // 25:00 
        console.log("seconds " + seconds)
        const mins = Math.floor(seconds / 60)
        const seconds_ = seconds % 60
        return mins.toString() + ":" + ((seconds_ == 0) ? "00" : seconds_.toString())
    }

    return (
        <div className="pomView">
            <ul>
                <button className="pomBut">Pomodoro</button>
                <button className="pomBut">Short Break</button>
                <button className="pomBut">Long Break</button>
            </ul>
            <h1>{dispSecondsAsMins(timer)}</h1>
            <div className="startDiv">
                {/* event handler onClick is function not function call */}
                <button className="startBut" onClick={toggleStart}>{!start ? "START" : "STOP"}</button>
                {start && <AiFillFastForward className="ff" onClick="" />}
            </div>
        </div>
    )
}

export default PomView

虽然clearInterval 在useEffect 的else 部分运行,但计时器仍在计时。我不确定是不是因为 useEffect 中的异步 setTimer 方法。我想知道我写的代码有什么问题。

【问题讨论】:

    标签: reactjs react-hooks use-effect use-state use-ref


    【解决方案1】:

    您将计时器 ref 存储在 tick 中,但每次组件重新渲染时,前一次渲染中的 tick 值都会丢失。您还应该将 tick 存储为 React ref。

    你也在改变 timer 状态。

    setTimer((timer) => {
      timer = timer - 1; // mutation
      return timer;
    });
    

    只返回当前值减1:setTimer((timer) =&gt; timer - 1);

    代码

    const PomView = () => {
      const [timer, setTimer] = useState(1500); // 25 minutes
      const [start, setStart] = useState(false);
      const firstStart = useRef(true);
      const tick = useRef(); // <-- React ref
    
      useEffect(() => {
        if (firstStart.current) {
          firstStart.current = !firstStart.current;
          return;
        }
    
        if (start) {
          tick.current = setInterval(() => { // <-- set tick ref current value
            setTimer((timer) => timer - 1);
          }, 1000);
        } else {
          clearInterval(tick.current); // <-- access tick ref current value
        }
    
        return () => clearInterval(tick.current); // <-- clear on unmount!
      }, [start]);
    
      ...
    };
    

    【讨论】:

    • 感谢您的帮助!我不确定在重新渲染期间保留了什么以及丢失了什么。每当 react 重新渲染函数组件的 return 部分时,无论状态变量如何变化都会运行。只有useStateuseRef 中的变量保留了之前的状态?
    • @calveeen 在您的代码中,是的,但也可以通过useMemouseCallback 钩子记住值,并且useReducer 在组件状态方面类似于useState。我建议阅读官方React docs 关于 React hooks 的内容。基本上所有的局部变量和函数都会在每个渲染周期重新声明。
    • setTimer((timer) =&gt; timer - 1); 和我写它的方式是否也有区别,因为两个代码都做同样的事情。我不确定你所说的改变计时器状态是什么意思
    • @calveeen 是的,有区别。虽然它们都完成了相同的操作,说timer = timer - 1 更新了先前的状态值,但它类似于执行timer--。最好声明一个新变量,更新并返回它,即const newTimer = timer - 1; return newTimer;。在 React 中,您希望避免状态突变。这里看起来是良性的,但如果状态更复杂,它确实会造成严重破坏并且难以追踪和调试。
    • 我明白了。谢谢!
    【解决方案2】:
    useEffect( () => {
        const tick= setInterval(fun, 1000);
        return ()=>{
          clearInterval(tick);
        }
    }, [])
    

    useEffect 有它自己的发布方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-02
      • 1970-01-01
      相关资源
      最近更新 更多