使用 React Hooks 的倒数计时器

IT技术 javascript reactjs timer components react-hooks
2021-05-24 11:24:22

所以定时器工作。如果我this.state使用特定的倒计时数字进行硬编码,则一旦页面加载,计时器就会开始倒计时。我希望时钟开始点击一个按钮倒计时并且具有改变功能nullstate,以一个随机生成的数字。我对 React 有点陌生。我知道useState()只设置初始值,但如果我使用点击事件,我该如何重置useState()我一直在尝试使用,setCountdown(ranNum)但它使我的应用程序崩溃。我相信答案是显而易见的,但我只是没有找到。

如果我没有提供足够的代码,请告诉我。我不想发布整个shebang。

这是我的代码:

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

export const Timer = ({ranNum, timerComplete}) => {
    const [ countDown, setCountdown ] = useState(ranNum)
    useEffect(() => {
        setTimeout(() => {
            countDown - 1 < 0 ? timerComplete() : setCountdown(countDown - 1)
        }, 1000)
    }, [countDown, timerComplete])
    return ( <p >Countdown: <span>{ countDown }</span> </p> )
}


  handleClick(){
    let newRanNum = Math.floor(Math.random() * 20);
    this.generateStateInputs(newRanNum)
    let current = this.state.currentImg;
    let next = ++current % images.length;
    this.setState({
      currentImg: next,
      ranNum: newRanNum
    })
  }

 <Timer ranNum={this.state.ranNum} timerComplete={() => this.handleComplete()} />
 <Button onClick={this.handleClick}  name='Generate Inputs' />
 <DisplayCount name='Word Count: ' count={this.state.ranNum} />

3个回答

您应该存储countDown在父组件中并将其传递给子组件。在父组件中,您应该使用一个变量来触发何时开始Timer你可以试试这个:

import React from "react";

export default function Timer() {
  const [initialTime, setInitialTime] = React.useState(0);
  const [startTimer, setStartTimer] = React.useState(false);

  const handleOnClick = () => {
    setInitialTime(5);
    setStartTimer(true);
  };

  React.useEffect(() => {
    if (initialTime > 0) {
      setTimeout(() => {
        console.log("startTime, ", initialTime);
        setInitialTime(initialTime - 1);
      }, 1000);
    }

    if (initialTime === 0 && startTimer) {
      console.log("done");
      setStartTimer(false);
    }
  }, [initialTime, startTimer]);

  return (
    <div>
      <buttononClick={handleOnClick}>
        Start
      </button>
      <Timer initialTime={initialTime} />
    </div>
  );
}

const Timer = ({ initialTime }) => {
  return <div>CountDown: {initialTime}</div>;
};

useState就像您说的那样设置初始值,但在您的情况下,我认为您不想countDownTimer. 原因ranNumundefined当您启动应用程序时,并被传递给Timer未定义的。Timer坐骑,useEffect将与值触发undefined这是一件好事,你不希望因为它会触发setTimeout我相信您可以存储countDown在 的父级中Timer,当从父级单击按钮时启动超时,并将countDownTimer作为props发送到 ,这将使组件方式更容易理解。

这是一个使用钩子和 setInterval 的简单实现

import React, {useState, useEffect, useRef} from 'react'
import './styles.css'

const STATUS = {
  STARTED: 'Started',
  STOPPED: 'Stopped',
}

export default function CountdownApp() {
  const [secondsRemaining, setSecondsRemaining] = useState(getRandomNum())
  const [status, setStatus] = useState(STATUS.STOPPED)

  const handleStart = () => {
    setStatus(STATUS.STARTED)
  }
  const handleStop = () => {
    setStatus(STATUS.STOPPED)
  }
  const handleRandom = () => {
    setStatus(STATUS.STOPPED)
    setSecondsRemaining(getRandomNum())
  }
  useInterval(
    () => {
      if (secondsRemaining > 0) {
        setSecondsRemaining(secondsRemaining - 1)
      } else {
        setStatus(STATUS.STOPPED)
      }
    },
    status === STATUS.STARTED ? 1000 : null,
    // passing null stops the interval
  )
  return (
    <div className="App">
      <h1>React Countdown Demo</h1>
      <button onClick={handleStart} type="button">
        Start
      </button>
      <button onClick={handleStop} type="button">
        Stop
      </button>
      <button onClick={handleRandom} type="button">
        Random
      </button>
      <div style={{padding: 20}}>{secondsRemaining}</div>
      <div>Status: {status}</div>
    </div>
  )
}

function getRandomNum() {
  return Math.floor(Math.random() * 20)
}

// source: https://overreacted.io/making-setinterval-declarative-with-react-hooks/
function useInterval(callback, delay) {
  const savedCallback = useRef()

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback
  }, [callback])

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current()
    }
    if (delay !== null) {
      let id = setInterval(tick, delay)
      return () => clearInterval(id)
    }
  }, [delay])
}

这是一个代码沙盒演示的链接:https ://codesandbox.io/s/react-countdown-demo-random-c9dm8 ? file =/ src/App.js