使用 React Hooks 的倒数计时器



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

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

这是我的代码:

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} />

您应该将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

就像你说的那样设置初始值,但在你的情况下,我认为你不想将countDown存储在Timer中。原因是ranNum在启动应用程序时undefined,并作为未定义传递到Timer。当Timer挂载时,useEffect将使用值undefined触发,这是您不想要的,因为它会触发setTimeout。我相信您可以将countDown存储在Timer的父级中,在从父级单击按钮时开始超时,并将countDown值作为道具发送到Timer,这将使组件更容易理解。

这是一个使用钩子和 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

相关内容

  • 没有找到相关文章

最新更新