【问题标题】:How make react countdown timer如何制作反应倒数计时器
【发布时间】:2019-12-12 20:57:03
【问题描述】:

我正在尝试用反应做倒数计时器。它基本上是从 10 到 0 的倒计时,当 0 时我会调用一些函数。

我为我找到了一些理想的例子:https://codesandbox.io/s/0q453m77nw?from-embed 但它是一个类组件,我不想用功能组件和钩子来做,但我不能。

我试过了:

function App() {
  const [seconds, setSeconds] = useState(10);
  useEffect(() => {
    setSeconds(setInterval(seconds, 1000));
  }, []);

  useEffect(() => {
    tick();
  });

  function tick() {
    if (seconds > 0) {
      setSeconds(seconds - 1)
    } else {
      clearInterval(seconds);
    }
  }

  return (

    <div className="App">
      <div
        {seconds}
      </div>
    </div>
  );
}

export default App;

它从 10 倒计时到 0 非常快,而不是 10 秒。 我哪里错了?

【问题讨论】:

  • 为什么要使用钩子?这基本上和常规组件一样多的代码。
  • @Mike'Pomax'Kamermans 虽然代码量大致相同(至少在当前设置中),但还有其他原因更喜欢函数式组件而不是类组件。函数式组件通常更容易阅读、更容易测试,而且 React 团队声称在未来的 React 版本中可能会有性能激励。

标签: javascript reactjs react-hooks countdown


【解决方案1】:

多个useEffect 挂钩似乎导致倒计时每秒运行一次以上。

这是一个简化的解决方案,我们检查 seconds 中的 useEffect 钩子,然后:

  • 使用setTimeout 1 秒后更新seconds,或
  • 做其他事情(倒计时结束时要调用的函数)

这种方法有一些缺点,见下文。

function App() {
  const [seconds, setSeconds] = React.useState(10);

  React.useEffect(() => {
    if (seconds > 0) {
      setTimeout(() => setSeconds(seconds - 1), 1000);
    } else {
      setSeconds('BOOOOM!');
    }
  });

  return (
    <div className="App">
      <div>
        {seconds}
      </div>
    </div>
  );
}

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

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

缺点

使用setInterval 的缺点是它可能会被停止 - 例如,组件被卸载、导航到不同的选项卡或关闭计算机。如果计时器需要更高的稳健性,更好的选择是将endTime 存储在状态中(如全局存储或上下文),并让您的组件根据endTime 检查当前时间以计算倒计时。

【讨论】:

    【解决方案2】:

    您关心精度吗?如果是这样,您不需要 setInterval。如果您不关心精度(而且您可能不关心),那么您可以安排在一段时间内调用tick(),而不是相反。

    const TimeoutComponent extends Component {
      constructor(props) {
        super(props);
        this.state = { countdown: 10 };
        this.timer = setInterval(() => this.tick(), props.timeout || 10000);
      }
    
      tick() {
        const current = this.state.countdown;
        if (current === 0) {
          this.transition();
        } else {
          this.setState({ countdown: current - 1 }); 
        } 
      }
    
      transition() {
        clearInterval(this.timer);
        // do something else here, presumably.
      }
    
      render() {
        return <div className="timer">{this.state.countDown}</div>;
      }
    }
    

    【讨论】:

      【解决方案3】:

      这有点取决于你的逻辑。在当前情况下,您在其中运行 tick 方法的 useEffect 在每个渲染上运行。您可以在下面找到一个简单的示例。

      function App() {
        const [seconds, setSeconds] = useState(10);
        const [done, setDone] = useState(false);
        const foo = useRef();
      
        useEffect(() => {
          function tick() {
              setSeconds(prevSeconds => prevSeconds - 1)
          }
          foo.current = setInterval(() => tick(), 1000)
        }, []);
      
        useEffect(() => {
          if (seconds  === 0) {
            clearInterval(foo.current);
            setDone(true);
          }
        }, [seconds])
      
        return (
      
          <div className="App">
          {seconds}
          {done && <p>Count down is done.</p>}
          </div>
        );
      }
      

      在第一个效果中,我们正在倒计时。使用回调一设置状态,因为间隔创建了一个闭包。在第二个效果中,我们正在检查我们的条件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-14
        相关资源
        最近更新 更多