리액트의 카운트다운 타이머
JavaScript에서 카운트다운 타이머를 많이 보았고 React에서 작업하기를 원했습니다.
온라인에서 찾은 다음 기능을 빌렸습니다.
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;
};
그리고 내가 직접 코드를 작성했어
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();
}
};
현재 클릭 시 화면에 표시되는 시간은 다음과 같이 설정됩니다.Time Remaining: 1 m : 0 s그러나 그것은 그것을 감소시키지 않는다.Time Remaining: 0 m : 59 s그리고 나서.Time Remaining: 0 m : 58 s/etc/setc/setc/setc/setc.
다른 파라미터로 함수를 다시 호출해야 할 것 같습니다.내가 어떻게 이걸 할 수 있을까?
편집: 몇 초에서 몇 분, 몇 초를 사용할 수 있는 기능을 원합니다.라고 말하는 것을 잊었습니다.
해야 한다setState(인터벌을 호출할 때마다) 남은 초마다.다음은 예를 제시하겠습니다.
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>
다음은 후크를 사용하는 해결책입니다. 타이머 컴포넌트입니다. 위의 논리를 후크로 복제합니다.
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;
@dan-abramov의 훅과 useInterval 구현을 사용한 간단한 구현을 다음에 나타냅니다.
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')
다음은 코드 앤 박스 구현입니다.https://codesandbox.io/s/react-countdown-demo-gtr4u?file=/src/App.js
시간이 지남에 따라 드리프트되는 카운트다운을 빼는 대신 Date.now()를 사용하여 카운트다운을 보여주는 기본적인 아이디어입니다.
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>
간단한 해상도:
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;
문제는 당신의 "이" 값에 있습니다.다른 컨텍스트에서 실행되므로 타이머 함수가 "상태" 프로포트에 액세스할 수 없습니다.다음과 같은 작업을 수행할 것을 권장합니다.
...
startTimer = () => {
let interval = setInterval(this.timer.bind(this), 1000);
this.setState({ interval });
};
보시다시피 타이머 기능에 "바인드" 메서드를 추가했습니다.이것에 의해, 타이머가 호출되었을 때에, 리액트 컴퍼넌트의 같은 「이」에 액세스 할 수 있게 됩니다(이것은 일반적으로 javascript를 사용할 때의 주된 문제/개선 사항입니다).
또 다른 옵션은 다른 화살표 기능을 사용하는 것입니다.
startTimer = () => {
let interval = setInterval(() => this.timer(), 1000);
this.setState({ interval });
};
사용자 입력 카운트다운
인터페이스 스크린샷
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;
저도 같은 문제가 있어서 카운트다운을 위해 이 npm 패키지를 찾았습니다.
패키지를 설치하다
npm install react-countdown --save또는yarn add react-countdown패키지를 파일로 가져오기
import Countdown from 'react-countdown';렌더 메서드 내에서 가져온 "카운트다운"을 호출하여 날짜를 전달합니다.
<Countdown date={new Date('2021-09-26T10:05:29.896Z').getTime()}>또는<Countdown date={new Date("Sat Sep 26 2021")}>
여기 당신을 위한 예가 있습니다.
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")
);
자세한 것은, https://www.npmjs.com/package/react-countdown 를 참조해 주세요.
기능 : 1) 시작 2) 리셋
기능 성분
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;
기능 컴포넌트를 사용할 때는 위의 코드를 사용하는 것이 좋습니다.
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>
</>
);
}
커스텀 훅을 사용한 심플한 실장은 다음과 같습니다.
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];
};
이 후크의 사용 예:
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>
);
}
다양한 시나리오에 맞게 쉽게 맞춤 가능한 24시간 카운트다운
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);
「 」의 setInterval을 사용하다는 '카운트다운 타이머'를 해서 할 수 요.requestAnimationFrame★★★★★★★★★★★★★★★★★★★★★★★★★★★예를 들어 일반적인 카운트다운타이머 컴포넌트는 다음과 같습니다.
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() {...}
}
다음은 React의 CountDown Timer TypeScript 버전입니다.마수드 형과 M형 코드를 사용했어게오르기예프
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;
@Masood의 답변 타이프스크립트/훅/짧은 버전
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;
다른 상태보다 타이머가 가장 높은 우선순위에 있는 것을 원치 않으므로 useTransition hook을 사용합니다. 지연은 180s = 3분 단위의 시간입니다.
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;
언급URL : https://stackoverflow.com/questions/40885923/countdown-timer-in-react
'programing' 카테고리의 다른 글
| JavaScript 라이브러리를 위한 최고의 LINQ는 무엇입니까? (0) | 2023.03.01 |
|---|---|
| 알 수 없는 속성 이름을 가진 JSON 스키마 (0) | 2023.03.01 |
| 가져오지 않고 JSX를 사용하여 반응에서 이미지 표시 (0) | 2023.03.01 |
| 각진 재료에서 라디오 버튼을 수평으로 정렬하려면 어떻게 해야 합니까? (0) | 2023.03.01 |
| Oracle SQL Developer에서 데이터베이스 이름을 쿼리하는 방법 (0) | 2023.03.01 |