admin管理员组文章数量:1134246
I have seen lots of countdown timers in JavaScript and wanted to get one working in React.
I have borrowed this function I found online:
secondsToTime(secs){
let hours = Math.floor(secs / (60 * 60));
let divisor_for_minutes = secs % (60 * 60);
let minutes = Math.floor(divisor_for_minutes / 60);
let divisor_for_seconds = divisor_for_minutes % 60;
let seconds = Math.ceil(divisor_for_seconds);
let obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
};
And then I have written this code myself
initiateTimer = () => {
let timeLeftVar = this.secondsToTime(60);
this.setState({ timeLeft: timeLeftVar })
};
startTimer = () => {
let interval = setInterval(this.timer, 1000);
this.setState({ interval: interval });
};
timer = () => {
if (this.state.timeLeft >0){
this.setState({ timeLeft: this.state.timeLeft -1 });
}
else {
clearInterval(this.state.interval);
//this.postToSlack();
}
};
Currently onclick it will set the time on screen to: Time Remaining: 1 m : 0 s
But it does not reduce it to Time Remaining: 0 m : 59 s
and then Time Remaining: 0 m : 58 s
etc etc
I think I need to call the function again with a different parameter. how can I go about doing this ?
Edit: I forgot to say, I would like the functionality so that I can use seconds to minutes & seconds
I have seen lots of countdown timers in JavaScript and wanted to get one working in React.
I have borrowed this function I found online:
secondsToTime(secs){
let hours = Math.floor(secs / (60 * 60));
let divisor_for_minutes = secs % (60 * 60);
let minutes = Math.floor(divisor_for_minutes / 60);
let divisor_for_seconds = divisor_for_minutes % 60;
let seconds = Math.ceil(divisor_for_seconds);
let obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
};
And then I have written this code myself
initiateTimer = () => {
let timeLeftVar = this.secondsToTime(60);
this.setState({ timeLeft: timeLeftVar })
};
startTimer = () => {
let interval = setInterval(this.timer, 1000);
this.setState({ interval: interval });
};
timer = () => {
if (this.state.timeLeft >0){
this.setState({ timeLeft: this.state.timeLeft -1 });
}
else {
clearInterval(this.state.interval);
//this.postToSlack();
}
};
Currently onclick it will set the time on screen to: Time Remaining: 1 m : 0 s
But it does not reduce it to Time Remaining: 0 m : 59 s
and then Time Remaining: 0 m : 58 s
etc etc
I think I need to call the function again with a different parameter. how can I go about doing this ?
Edit: I forgot to say, I would like the functionality so that I can use seconds to minutes & seconds
Share Improve this question edited Aug 27, 2019 at 12:57 Anfuca 1,3291 gold badge14 silver badges27 bronze badges asked Nov 30, 2016 at 10:27 The wormThe worm 5,89014 gold badges39 silver badges51 bronze badges 6- 1 One of the React documentation examples is a clock that updates itself, seems like it would be fairly useful... – T.J. Crowder Commented Nov 30, 2016 at 10:31
- @T.J.Crowder it is semi helpful. they are just getting a time though as can return it through componentDidMount whereas I only want to extract seconds and minutes from a starting position.. – The worm Commented Nov 30, 2016 at 10:50
- Perhaps you could put a runnable minimal reproducible example in the question using Stack Snippets, which support React and JSX, so we could see the problem in action. – T.J. Crowder Commented Nov 30, 2016 at 10:53
- @T.J.Crowder finding it very difficult to create one in JSfiddle as I am using many components with many props across many files – The worm Commented Nov 30, 2016 at 11:04
- @T.J.Crowder from the question, what makes sense to you? (to see if I can add more knowledge to things explained less well) – The worm Commented Nov 30, 2016 at 11:04
20 Answers
Reset to default 100You have to setState
every second with the seconds remaining (every time the interval is called). Here's an example:
class Example extends React.Component {
constructor() {
super();
this.state = { time: {}, seconds: 5 };
this.timer = 0;
this.startTimer = this.startTimer.bind(this);
this.countDown = this.countDown.bind(this);
}
secondsToTime(secs){
let hours = Math.floor(secs / (60 * 60));
let divisor_for_minutes = secs % (60 * 60);
let minutes = Math.floor(divisor_for_minutes / 60);
let divisor_for_seconds = divisor_for_minutes % 60;
let seconds = Math.ceil(divisor_for_seconds);
let obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
}
componentDidMount() {
let timeLeftVar = this.secondsToTime(this.state.seconds);
this.setState({ time: timeLeftVar });
}
startTimer() {
if (this.timer == 0 && this.state.seconds > 0) {
this.timer = setInterval(this.countDown, 1000);
}
}
countDown() {
// Remove one second, set state so a re-render happens.
let seconds = this.state.seconds - 1;
this.setState({
time: this.secondsToTime(seconds),
seconds: seconds,
});
// Check if we're at zero.
if (seconds == 0) {
clearInterval(this.timer);
}
}
render() {
return(
<div>
<button onClick={this.startTimer}>Start</button>
m: {this.state.time.m} s: {this.state.time.s}
</div>
);
}
}
ReactDOM.render(<Example/>, document.getElementById('View'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="View"></div>
Here is a solution using hooks, Timer component, I'm replicating same logic above with hooks
import React from 'react'
import { useState, useEffect } from 'react';
const Timer = (props:any) => {
const {initialMinute = 0,initialSeconds = 0} = props;
const [ minutes, setMinutes ] = useState(initialMinute);
const [seconds, setSeconds ] = useState(initialSeconds);
useEffect(()=>{
let myInterval = setInterval(() => {
if (seconds > 0) {
setSeconds(seconds - 1);
}
if (seconds === 0) {
if (minutes === 0) {
clearInterval(myInterval)
} else {
setMinutes(minutes - 1);
setSeconds(59);
}
}
}, 1000)
return ()=> {
clearInterval(myInterval);
};
});
return (
<div>
{ minutes === 0 && seconds === 0
? null
: <h1> {minutes}:{seconds < 10 ? `0${seconds}` : seconds}</h1>
}
</div>
)
}
export default Timer;
Here is a simple implementation using hooks and useInterval implementation of @dan-abramov
import React, {useState, useEffect, useRef} from 'react'
import './styles.css'
const STATUS = {
STARTED: 'Started',
STOPPED: 'Stopped',
}
const INITIAL_COUNT = 120
export default function CountdownApp() {
const [secondsRemaining, setSecondsRemaining] = useState(INITIAL_COUNT)
const [status, setStatus] = useState(STATUS.STOPPED)
const secondsToDisplay = secondsRemaining % 60
const minutesRemaining = (secondsRemaining - secondsToDisplay) / 60
const minutesToDisplay = minutesRemaining % 60
const hoursToDisplay = (minutesRemaining - minutesToDisplay) / 60
const handleStart = () => {
setStatus(STATUS.STARTED)
}
const handleStop = () => {
setStatus(STATUS.STOPPED)
}
const handleReset = () => {
setStatus(STATUS.STOPPED)
setSecondsRemaining(INITIAL_COUNT)
}
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={handleReset} type="button">
Reset
</button>
<div style={{padding: 20}}>
{twoDigits(hoursToDisplay)}:{twoDigits(minutesToDisplay)}:
{twoDigits(secondsToDisplay)}
</div>
<div>Status: {status}</div>
</div>
)
}
// 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://stackoverflow.com/a/2998874/1673761
const twoDigits = (num) => String(num).padStart(2, '0')
Here is the codesandbox implementation: https://codesandbox.io/s/react-countdown-demo-gtr4u?file=/src/App.js
Basic idea showing counting down using Date.now() instead of subtracting one which will drift over time.
class Example extends React.Component {
constructor() {
super();
this.state = {
time: {
hours: 0,
minutes: 0,
seconds: 0,
milliseconds: 0,
},
duration: 2 * 60 * 1000,
timer: null
};
this.startTimer = this.start.bind(this);
}
msToTime(duration) {
let milliseconds = parseInt((duration % 1000));
let seconds = Math.floor((duration / 1000) % 60);
let minutes = Math.floor((duration / (1000 * 60)) % 60);
let hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
hours = hours.toString().padStart(2, '0');
minutes = minutes.toString().padStart(2, '0');
seconds = seconds.toString().padStart(2, '0');
milliseconds = milliseconds.toString().padStart(3, '0');
return {
hours,
minutes,
seconds,
milliseconds
};
}
componentDidMount() {}
start() {
if (!this.state.timer) {
this.state.startTime = Date.now();
this.timer = window.setInterval(() => this.run(), 10);
}
}
run() {
const diff = Date.now() - this.state.startTime;
// If you want to count up
// this.setState(() => ({
// time: this.msToTime(diff)
// }));
// count down
let remaining = this.state.duration - diff;
if (remaining < 0) {
remaining = 0;
}
this.setState(() => ({
time: this.msToTime(remaining)
}));
if (remaining === 0) {
window.clearTimeout(this.timer);
this.timer = null;
}
}
render() {
return ( <
div >
<
button onClick = {
this.startTimer
} > Start < /button> {
this.state.time.hours
}: {
this.state.time.minutes
}: {
this.state.time.seconds
}. {
this.state.time.milliseconds
}:
<
/div>
);
}
}
ReactDOM.render( < Example / > , document.getElementById('View'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="View"></div>
simple resolution:
import React, { useState, useEffect } from "react";
const Timer = ({ delayResend = "180" }) => {
const [delay, setDelay] = useState(+delayResend);
const minutes = Math.floor(delay / 60);
const seconds = Math.floor(delay % 60);
useEffect(() => {
const timer = setInterval(() => {
setDelay(delay - 1);
}, 1000);
if (delay === 0) {
clearInterval(timer);
}
return () => {
clearInterval(timer);
};
});
return (
<>
<span>
{minutes}:{seconds}
</span>
</>
);
};
export default Timer;
Countdown of user input
Interface Screenshot
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
hours: 0,
minutes: 0,
seconds:0
}
this.hoursInput = React.createRef();
this.minutesInput= React.createRef();
this.secondsInput = React.createRef();
}
inputHandler = (e) => {
this.setState({[e.target.name]: e.target.value});
}
convertToSeconds = ( hours, minutes,seconds) => {
return seconds + minutes * 60 + hours * 60 * 60;
}
startTimer = () => {
this.timer = setInterval(this.countDown, 1000);
}
countDown = () => {
const { hours, minutes, seconds } = this.state;
let c_seconds = this.convertToSeconds(hours, minutes, seconds);
if(c_seconds) {
// seconds change
seconds ? this.setState({seconds: seconds-1}) : this.setState({seconds: 59});
// minutes change
if(c_seconds % 60 === 0 && minutes) {
this.setState({minutes: minutes -1});
}
// when only hours entered
if(!minutes && hours) {
this.setState({minutes: 59});
}
// hours change
if(c_seconds % 3600 === 0 && hours) {
this.setState({hours: hours-1});
}
} else {
clearInterval(this.timer);
}
}
stopTimer = () => {
clearInterval(this.timer);
}
resetTimer = () => {
this.setState({
hours: 0,
minutes: 0,
seconds: 0
});
this.hoursInput.current.value = 0;
this.minutesInput.current.value = 0;
this.secondsInput.current.value = 0;
}
render() {
const { hours, minutes, seconds } = this.state;
return (
<div className="App">
<h1 className="title"> (( React Countdown )) </h1>
<div className="inputGroup">
<h3>Hrs</h3>
<input ref={this.hoursInput} type="number" placeholder={0} name="hours" onChange={this.inputHandler} />
<h3>Min</h3>
<input ref={this.minutesInput} type="number" placeholder={0} name="minutes" onChange={this.inputHandler} />
<h3>Sec</h3>
<input ref={this.secondsInput} type="number" placeholder={0} name="seconds" onChange={this.inputHandler} />
</div>
<div>
<button onClick={this.startTimer} className="start">start</button>
<button onClick={this.stopTimer} className="stop">stop</button>
<button onClick={this.resetTimer} className="reset">reset</button>
</div>
<h1> Timer {hours}: {minutes} : {seconds} </h1>
</div>
);
}
}
export default App;
The problem is in your "this" value. Timer function cannot access the "state" prop because run in a different context. I suggest you to do something like this:
...
startTimer = () => {
let interval = setInterval(this.timer.bind(this), 1000);
this.setState({ interval });
};
As you can see I've added a "bind" method to your timer function. This allows the timer, when called, to access the same "this" of your react component (This is the primary problem/improvement when working with javascript in general).
Another option is to use another arrow function:
startTimer = () => {
let interval = setInterval(() => this.timer(), 1000);
this.setState({ interval });
};
I had the same problem and I found this npm package for a countdown.
install the package
npm install react-countdown --save
oryarn add react-countdown
import the package to your file
import Countdown from 'react-countdown';
call the imported "Countdown" inside a render method and pass a date
<Countdown date={new Date('2021-09-26T10:05:29.896Z').getTime()}>
or<Countdown date={new Date("Sat Sep 26 2021")}>
Here is an example for you.
import React from "react";
import ReactDOM from "react-dom";
import Countdown from "react-countdown";
// Random component
const Completionist = () => <span>You are good to go!</span>;
ReactDOM.render(
<Countdown date={new Date('2021-09-26T10:05:29.896Z').getTime()}>
<Completionist />
</Countdown>,
document.getElementById("root")
);
you can see the detailed document here https://www.npmjs.com/package/react-countdown
Usually there is no need for extreme precision so you can use setTimeout, but if you need some finesse with time handling you can change useEffect's callback, just have to set timer to needed value. Still, it's a component rerender every second or less.
const {useEffect, useState} = React;
const Countdown = (props) => {
const [timer, setTimer] = useState(120); //in seconds
const timerToString = () => {
let hours = ('0' + Math.floor(timer/3600)).slice(-2);
let minutes = ('0' + Math.floor(timer/60)).slice(-2);
let seconds = ('0' + timer%60).slice(-2);
return /*hours + ":" +*/ minutes + ":" + seconds;
}
useEffect(()=>{
if(timer > 0){
setTimeout(()=>{
setTimer(timer-1);
}, 1000)
}
}, [timer]);
return (
<div> {timerToString()}</div>
)
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Countdown />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
functionality : 1)Start 2)Reset
functional component
import {useState, useCallback} from 'react';
const defaultCount = 10;
const intervalGap = 300;
const Counter = () => {
const [timerCount, setTimerCount] = useState(defaultCount);
const startTimerWrapper = useCallback((func)=>{
let timeInterval: NodeJS.Timer;
return () => {
if(timeInterval) {
clearInterval(timeInterval)
}
setTimerCount(defaultCount)
timeInterval = setInterval(() => {
func(timeInterval)
}, intervalGap)
}
}, [])
const timer = useCallback(startTimerWrapper((intervalfn: NodeJS.Timeout) => {
setTimerCount((val) => {
if(val === 0 ) {
clearInterval(intervalfn);
return val
}
return val - 1
})
}), [])
return <>
<div> Counter App</div>
<div> <button onClick={timer}>Start/Reset</button></div>
<div> {timerCount}</div>
</>
}
export default Counter;
When you are using functional components the above code is a good option to do it:
import React, { useState, useEffect } from "react";
import { MessageStrip } from "@ui5/webcomponents-react";
import "./Timer.scss";
const nMinuteSeconds = 60;
const nSecondInMiliseconds = 1000;
const convertMinutesToMiliseconds = (minute) =>
minute * nMinuteSeconds * nSecondInMiliseconds;
const convertMilisecondsToHour = (miliseconds) => new Date(miliseconds).toISOString().slice(11, -5);
export default function Counter({ minutes, onTimeOut }) {
let [timerCount, setTimerCount] = useState(
convertMinutesToMiliseconds(minutes)
);
let interval;
useEffect(() => {
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => {
if (timerCount === 0 && interval) {
onTimeOut();
clearInterval(interval);
}
setTimerCount((timerCount -= nSecondInMiliseconds));
}, nSecondInMiliseconds);
}, []);
return (
<>
<MessageStrip design="Information" hide-close-button>
Time left: {convertMilisecondsToHour(timerCount)}
</MessageStrip>
</>
);
}
Here's a simple implementation using a custom hook:
import * as React from 'react';
// future time from where countdown will start decreasing
const futureTime = new Date( Date.now() + 5000 ).getTime(); // adding 5 seconds
export const useCountDown = (stop = false) => {
const [time, setTime] = React.useState(
futureTime - Date.now()
);
// ref to store interval which we can clear out later
// when stop changes through parent component (component that uses this hook)
// causing the useEffect callback to trigger again
const intervalRef = React.useRef<NodeJS.Timer | null>(null);
React.useEffect(() => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
return;
}
const interval = intervalRef.current = setInterval(() => {
setTime(futureTime - Date.now());
}, 1000);
return () => clearInterval(interval);
}, [stop]);
return getReturnValues(time);
};
const getReturnValues = (countDown: number) => {
const days = Math.floor(countDown / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((countDown % (1000 * 60)) / 1000);
return [days, hours, minutes, seconds];
};
Example of using this hook:
function App() {
const [timerStopped, stopTimer] = React.useState(false);
const [,hours,minutes,seconds] = useCountDown(timerStopped);
// to stop the interval
if( !timerStopped && minutes + seconds <= 0 ) {
stopTimer(true);
}
return (
<div className="App">
Time Left: {hours}:{minutes}:{seconds}
{ timerStopped ? (
<h1>Time's up</h1>
) : null }
</div>
);
}
A simple 24-hour countdown that can easily be customized to fit different scenarios
setInterval(function time() {
let d = new Date();
let hours = 24 - d.getHours();
let min = 60 - d.getMinutes();
if ((min + "").length == 1) {
min = "0" + min;
}
let sec = 60 - d.getSeconds();
if ((sec + "").length == 1) {
sec = "0" + sec;
}
setState(hours + ":" + min + ":" + sec);
}, 1000);
Here is my solution React + typescript:
When it comes to JavaScript timers, it's important to note that they are not always guaranteed to be perfectly precise. The accuracy of timers can vary depending on various factors, including the performance of the underlying system and the load on the browser.
Instead of relying on setInterval with a fixed interval of 1000 milliseconds, we can use the performance.now()
method to calculate the actual time elapsed between each tick. This allows to account for any delay introduced by the execution of other code.
By using a smaller interval (e.g., 10 milliseconds), we increase the frequency of checks for more precision.
interface ICountdownTimerProps {
minutes: number
}
export const CountdownTimer = ({ minutes }: ICountdownTimerProps) => {
let startTimestamp = performance.now()
const secondBase = 1000
const minuteBase = 60 * secondBase
const [timeLeft, setTimeLeft] = useState(minutes * minuteBase)
const secondTick = () => {
setTimeLeft((prevTimeLeft) => {
const timeLeftAfterTick = prevTimeLeft - secondBase
if (timeLeftAfterTick < 0) {
return prevTimeLeft
}
return timeLeftAfterTick
})
}
const formatTime = (time: number, type: "seconds" | "minutes") => {
switch (type) {
case "seconds":
case "minutes":
return time.toString().padStart(2, "0")
default:
return ""
}
}
const getMinutes = (timeLeft: number) => {
return formatTime(Math.floor(timeLeft / minuteBase), "minutes")
}
const getSeconds = (timeLeft: number) => {
return formatTime(Math.floor((timeLeft % minuteBase) / secondBase), "seconds")
}
useEffect(() => {
const interval = setInterval(() => {
const currentTimestamp = performance.now()
const elapsed = currentTimestamp - startTimestamp
if (elapsed >= secondBase) {
startTimestamp = currentTimestamp
secondTick()
}
}, 10)
return () => {
clearInterval(interval)
}
}, [])
return (
<div className="countdown-timer">
<span>{getMinutes(timeLeft)}</span>:<span>{getSeconds(timeLeft)}</span>
</div>
)
}
> useEffect(() => {
> const timer = setInterval(() => {
> if (countdown > 1) {
> setCountdown(countdown - 1);
> } else {
> clearInterval(timer);
> }
> }, 1000);
>
> return () => {
> clearInterval(timer);
> }; }, [countdown]);
The one downside with setInterval
is that it can slow down the main thread. You can do a countdown timer using requestAnimationFrame
instead to prevent this. For example, this is my generic countdown timer component:
class Timer extends Component {
constructor(props) {
super(props)
// here, getTimeRemaining is a helper function that returns an
// object with { total, seconds, minutes, hours, days }
this.state = { timeLeft: getTimeRemaining(props.expiresAt) }
}
// Wait until the component has mounted to start the animation frame
componentDidMount() {
this.start()
}
// Clean up by cancelling any animation frame previously scheduled
componentWillUnmount() {
this.stop()
}
start = () => {
this.frameId = requestAnimationFrame(this.tick)
}
tick = () => {
const timeLeft = getTimeRemaining(this.props.expiresAt)
if (timeLeft.total <= 0) {
this.stop()
// ...any other actions to do on expiration
} else {
this.setState(
{ timeLeft },
() => this.frameId = requestAnimationFrame(this.tick)
)
}
}
stop = () => {
cancelAnimationFrame(this.frameId)
}
render() {...}
}
Here is a TypeScript version of CountDown Timer in React. I used code of brother Masood and M.Georgiev
import React, {useState, useEffect, useCallback} from "react";
const Minute_to_Seconds = 60;
const Seconds_to_milliseconds = 1000;
export interface CounterProps {
minutes:number,
statusAlert: (status: string)=>void,
}
export interface TimerProps {
initialMinute: number,
initialSeconds: number,
}
const Counter: React.FC<CounterProps> = (props) => {
const convert_Minutes_To_MiliSeconds = (minute:number) => {
return minute * Minute_to_Seconds * Seconds_to_milliseconds;
}
const convert_Mili_Seconds_To_Hour = (miliseconds:number) => {
return new Date(miliseconds).toISOString().slice(11, -5);
}
const convert_Mili_Seconds_To_Minute = (miliseconds:number) => {
return new Date(miliseconds).toISOString().slice(11, -5);
}
const [timer_State, setTimer_State]=useState(0);
const [timerCount, setTimerCount] = useState(convert_Minutes_To_MiliSeconds(props.minutes));
useEffect(() => {
if (timerCount > 0) {
const interval = setInterval(() => {
if (timer_State === 0) {
props.statusAlert("start");
setTimer_State(1);
}
let tempTimerCount = timerCount;
tempTimerCount -= Seconds_to_milliseconds;
setTimerCount(tempTimerCount);
},
(timer_State === 0)
? 0
: Seconds_to_milliseconds
);
return () => {
clearInterval(interval);
}
}
else{
props.statusAlert("end");
}
}, [
timer_State,
timerCount,
props,
]);
return (
<p>
Time left: {convert_Mili_Seconds_To_Hour(timerCount)}
</p>
);
}
const Timer: React.FC<TimerProps> = (props) => {
const [ minutes, setMinutes ] = useState(props.initialMinute);
const [seconds, setSeconds ] = useState(props.initialSeconds);
useEffect(()=>{
const myInterval = setInterval(() => {
if (seconds > 0) {
setSeconds(seconds - 1);
}
if (seconds === 0) {
if (minutes === 0) {
clearInterval(myInterval)
} else {
setMinutes(minutes - 1);
setSeconds(59);
}
}
}, 1000)
return ()=> {
clearInterval(myInterval);
};
});
return (
<div>
{ ((minutes === 0) && (seconds === 0))
? "Press F5 to Refresh"
: <h1> {minutes}:{seconds < 10 ? `0${seconds}` : seconds}</h1>
}
</div>
)
}
const RCTAPP=()=> {
const status_Alert2=(status: string)=> {
console.log("__________________________==================== status: ", status);
if (status==="start"){
alert("Timer started");
}
else{
alert("Time's up");
}
}
return (
<div style={{textAlign: "center"}}>
<Counter
minutes={1}
// minutes={0.1}
statusAlert={status_Alert2}
/>
<Timer
initialMinute={0}
initialSeconds={30}
/>
</div>
);
}
export default RCTAPP;
Typescript/Hooks/Shorter version of @Masood's answer
import { useState, useEffect } from 'react';
type Props = {
initMin: number,
initSecs: number
};
const Timer = ({initMins, initSecs}: Props) => {
// Combining useState values together for brevity
const [ [mins, secs], setCountdown ] = useState([initMins, initSecs]);
/**
* Triggers each second and whenever mins/seconds updates itself.
*/
useEffect(() => {
// Timer that decrements itself each second and updates the mins/seconds downwards
let timerInterval = setInterval(() => {
// Countdown timer up, clear timer and do nothing
if (mins === 0 && secs === 0) {
clearInterval(timerInterval);
} else if (secs === 0) {
// Might be correct to set seconds to 59, but not sure
// should decrement from 60 seconds yeah?
setCountdown([mins - 1, 60]);
} else {
setCountdown([mins, secs - 1]);
}
}, 1000);
return () => {
clearInterval(timerInterval);
};
}, [mins, secs]);
return (
<div>
{ mins === 0 && secs === 0
? null
: <h1> {mins}:{secs < 10 ? `0${secs}` : secs}</h1>
}
</div>
)
}
export default Timer;
As we don't want the timer at the highest priority than other states so we will use useTransition hook. delay is the time in seconds 180s = 3min.
import React, { useState, useEffect, useTransition } from "react";
const Timer = ({ delayResend = "180" }) => {
const [delay, setDelay] = useState(+delayResend);
const [minutes, setMinutes] = useState(0);
const [seconds, setSeconds] = useState(0);
const [isPending, startTransition] = useTransition();
useEffect(() => {
const timer = setInterval(() => {
startTransition(() => {
setDelay(delay - 1);
setMinutes(Math.floor(delay / 60));
setSeconds(Math.floor(delay % 60));
});
}, 1000);
if (delay === 0) {
clearInterval(timer);
}
return () => {
clearInterval(timer);
};
});
return (
<>
<span>
{minutes}:{seconds}
</span>
</>
);
};
export default Timer;
import { useEffect, useMemo, useState } from "react";
const SECOND = 1000;
const MINUTE = SECOND * 60;
const HOUR = MINUTE * 60;
const DAY = HOUR * 24;
export const Timer = ({ deadline = new Date().toString() }) => {
const parsedDeadline = useMemo(() => Date.parse(deadline), [deadline]);
const [time, setTime] = useState(parsedDeadline - Date.now());
useEffect(() => {
const interval = setInterval(
() => setTime(parsedDeadline - Date.now()),
1000,
);
return () => clearInterval(interval);
}, [parsedDeadline]);
return (
<div className="timer">
{Object.entries({
Days: time / DAY,
Hours: (time / HOUR) % 24,
Minutes: (time / MINUTE) % 60,
Seconds: (time / SECOND) % 60,
}).map(([label, value]) => (
<div key={label} className="col-4">
<div className="box">
<p>{`${Math.floor(value)}`.padStart(2, "0")}</p>
<span className="text">{label}</span>
</div>
</div>import { useEffect, useMemo, useState } from "react";
const SECOND = 1000;
const MINUTE = SECOND * 60;
const HOUR = MINUTE * 60;
const DAY = HOUR * 24;
export const Timer = ({ deadline = new Date().toString() }) => {
const parsedDeadline = useMemo(() => Date.parse(deadline), [deadline]);
const [time, setTime] = useState(parsedDeadline - Date.now());
useEffect(() => {
const interval = setInterval(
() => setTime(parsedDeadline - Date.now()),
1000,
);
return () => clearInterval(interval);
}, [parsedDeadline]);
return (
<div className="timer">
{Object.entries({
Days: time / DAY,
Hours: (time / HOUR) % 24,
Minutes: (time / MINUTE) % 60,
Seconds: (time / SECOND) % 60,
}).map(([label, value]) => (
<div key={label} className="col-4">
<div className="box">
<p>{`${Math.floor(value)}`.padStart(2, "0")}</p>
<span className="text">{label}</span>
</div>
</div>
))}
</div>
);
};
))}
</div>
);
};
本文标签: javascriptCountdown timer in ReactStack Overflow
版权声明:本文标题:javascript - Countdown timer in React - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736771426a1952106.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论