使用 setInterval() 制作带有 ReactJS 挂钩的会话计时器:错误消息、不规则计数和 "null" 输出

Using setInterval() to make a session timer with ReactJS hooks: error messages, irregular counting and "null" output

我正在尝试制作一个计时器,该计时器将在用户加载主页时开始(在 00:00),每秒递增一次,并在用户转到其他页面时重置为 00:00页。

这是我的最新代码:

  const [ timerCount, increaseTimer ] = useState(0);

  function timerItself() {
    function timerIncrement() {
      var currentTimerCount = timerCount;
      increaseTimer(currentTimerCount + 1);
    };
    setInterval(() => timerIncrement, 1000);
    clearInterval(timerIncrement);
    return;
  }

  function pageHome() {
    var timerMinutes = JSON.stringify(Math.floor(timerItself / 60));
    var timerSeconds = JSON.stringify(timerItself % 60);

    timerItself();

    return (
      <div classname="App">
        <header className="App-header">
          <h1>Hello world!</h1>
          <img src={logo} className="App-logo" alt="logo" />
          <p>{timerMinutes.padStart(2, '0')}:{timerSeconds.padStart(2, '0')}</p>
          <p>
            <button onClick={(() => changePage('About'))}>
              About
            </button>
            <button onClick={(() => changePage('Help'))}>
              Help
            </button>
          </p>
        </header>
      </div>
    );
  }

之前我将 setInterval() 包裹在 const 中,如下所示: const timer = setInterval(() => increaseTimer(timerCount + 1), 1000); 并尝试将 clearInterval() 放入按钮的 onClick 函数中,认为这只会重置计时器。

输出有奇怪的行为:计时器会以不规则的间隔计数,从 00:03 而不是 00:00 开始非常慢,然后变得非常快。显示的时间顺序是一致的,但我无法将其识别为数学序列:3、7、11、29……81……并在大约 30 秒内达到非常高的数字。无法弄清楚代码中数字的来源。

我做了更多的阅读,得出的结论是 timer 同时启动了多个计时器,并以某种方式将它们的输出加在一起。所以我写了 timerItself() ,里面有一个 clearInterval() 我假设它也会每秒触发一次以清除旧计时器并为新计时器清理。但是现在根本没有计数,计时器的字符串化输出是“null:null”。

我对 clearInterval() 应该放在哪里感到困惑,因为我认为这是解决这个问题的主要关键。如果有任何帮助,我将不胜感激。

要创建一个在渲染组件时开始的间隔,您可以使用 useEffect Hook,并将 timerCount 作为依赖项。因为当您第一次渲染组件时,您想要启动计数器,并且随着您增加计时器,您希望间隔继续。

import React, { useState, useEffect } from 'react';

export default function Home() {
  const [timerCount, increaseTimer] = useState(0);

  useEffect(() => {
    let myTimerId;

    myTimerId = setInterval(() => {
      increaseTimer(timerCount + 1);
    }, 1000);

    return () => clearInterval(myTimerId);
  }, [timerCount]);

  return (
    <div>
      <h1>Timer {timerCount}</h1>
    </div>
  );
}


export function Other() {
  return <div>This is the other Page</div>;
}

useEffect Hook 返回的箭头函数是一个清理函数,它将清除 TheInterval 以防止内存泄漏。

这里是 App.js 组件

import React from 'react';
import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom';
import Home, { Other } from './Home';

export default function App() {
  return (
    <>
      <Router>
        <ul>
          <li>
            <Link to="/home">Home</Link>
          </li>

          <li>
            <Link to="/other">Other</Link>
          </li>
        </ul>

        <Switch>
          <Route exact path="/home" component={Home} />
          <Route exact path="/other" component={Other} />
        </Switch>
      </Router>
    </>
  );
}

我使用了一个名为 react-router-dom 的库来在您的应用程序的路径之间导航,而无需发出新请求。

参考:https://reactrouter.com/web/guides/quick-start

使用效果:https://reactjs.org/docs/hooks-effect.html

// 另外我建议你以这种方式命名更新状态的函数:

const [myExampleState, setMyExampleState] = useState('Example1');

setMyExampleState('Example2');
  • 希望我有所帮助。 (同时查看生命周期以更好地了解 React 的工作原理;D)

是的,setinterval 不能很好地处理 hooks。即使使用 useEffect 解决方案,您也会浪费时间。如果间隔最多为 3/4 秒,则出于任何原因的重新渲染将破坏并重新创建计时器,使其再次回到零。大量重新渲染,间隔开,有效地使计时器瘫痪。

这可能是将计时器放在组件外部的一个好例子。它仍然在组件的范围内,不受其控制。对于导航上调用的任何函数都是一样的。

已解决!

这段代码对我的用途来说效果很好:

  const [ timer, changeTimer ] = useState(0);
  var currentTimerCount = timer;

  useEffect(() => {
    if (page !== 'Home')
      changeTimer(0);
    if (page == 'Home')
      currentTimerCount = setInterval(() => {changeTimer(prevTimer => prevTimer + 1)}, 1000);
      return () => clearInterval(currentTimerCount);
  });

  var timerMinutes = JSON.stringify(Math.floor(currentTimerCount / 60));
  var timerSeconds = JSON.stringify(currentTimerCount % 60);

然后像以前一样调用 timerMinutes 和 timerSeconds。

感谢所有回答的人!