I'm working on an application and I have a timer with the current date, hour, minutes and seconds displayed. It is incremented every second. What I want to do is to stop the timer on a button click, but I'm not sure why the clearInterval() function doesn't work. My code is:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
timer: "",
};
}
componentDidMount() {
//current time
setInterval(() => {
this.setState({
currentTime : new Date().toLocaleString()
})
}, 1000)
}
stopTimer = () => {
clearInterval(this.state.currentTime)
}
render() {
return (
<div>
<h4>Current time: {this.state.currentTime}</h4>
<Button onClick={this.stopTimer}>Stop timer</Button>
</div>
)
}
}
setInterval() result is the intervalID and you should use this for clearInterval().
this.state = {
...
intervalId: ""
};
...
const intervalId = setInterval(() => {
...
}, 1000);
this.setState({ intervalId });
...
clearInterval(this.state.intervalId);
You are using the clearInterval on wrong variable . You should do this.
I'm working on an application and I have a timer with the current date, hour, minutes and seconds displayed. It is incremented every second. What I want to do is to stop the timer on a button click, but I'm not sure why the clearInterval() function doesn't work. My code is:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
timer: "",
intervalId: ""
};
}
componentDidMount() {
//current time
const intervalId = setInterval(() => {
this.setState({
currentTime : new Date().toLocaleString()
})
}, 1000);
this.setState({intervalId})
}
stopTimer = () => {
if (this.state.intervalId) {
clearInterval(this.state.intervelId)
}
}
render() {
return (
<div>
<h4>Current time: {this.state.currentTime}</h4>
<Button onClick={this.stopTimer}>Stop timer</Button>
</div>
)
}
}
Related
Hey i want to extract only the seconds from the current date and i am kind of blocked
This is my component and it only returns the whole date:
class Clock extends Component {
constructor(props) {
super(props);
this.state = {
time: new Date().toLocaleString()
};
}
componentDidMount() {
this.intervalID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
tick() {
this.setState({
time: new Date().toLocaleString()
});
}
render() {
return (
<div>{this.state.time}.</div>
);
}
}
export default Clock;
constructor(props) {
super(props);
this.state = {
time: new Date().getSeconds()
};
}
You can use the getSeconds(); method on the date object
I have a react component that accepts an array of 2 moment.js time formats that will be used as props in the component to then render the time in a small clock widget in the format passed in. I need to dynamically calculate the refresh interval depending on time format passed in and being utilized. So if only needing seconds due to time format passed in being a second counter, interval refreshes every second, if only needing minutes, refresh rate will be around every minute. Couldn't find anything that has helped yet so asking for some advice. Thanks in advance. Here is my code, sorry it looks so rough right now.
import React, { Component } from 'react';
import moment from 'moment';
// Styles
import Style from 'Components/Header/Header.module.css';
class ClockWidget extends Component {
constructor() {
super()
this.state = {
time: moment().format('hh:mm a'),
toggle: true,
timerID: null
}
this.handleClick = this.handleClick.bind(this);
}
setTime() {
this.timerID = setInterval(() => {
if (this.state.toggle == true) {
this.setState({
time: moment().format(this.props.timeFormat[0])
})
} else if (this.state.toggle == false) {
this.setState({
time: moment().format(this.props.timeFormat[1])
})
}
console.log("State: ", this.state);
}, this.state.toggle ? 1000 : 60000);
}
componentDidMount() {
this.setTime();
}
handleClick() {
if (this.state.toggle) {
this.setState({ toggle: false })
} else {
this.setState({ toggle: true })
}
}
render() {
return (
<div onClick={this.handleClick}>
<p className={Style.action}> {this.state.time} </p>
</div>
)}
}
ClockWidget.defaultProps = {
timeFormat: ['hh:mm a', 'HH:mm']
}
export default ClockWidget;
do some changes...
in your state {
this.timerId = null
}
.
.
.
.
setTime(time = null, toggle = null) {
this.timerId = setInterval(() => {
if (this.state.toggle) {
this.setState({
time: moment().format(this.props.timeFormat[0]),
})
} else if (!this.state.toggle) {
this.setState({
time: moment().format(this.props.timeFormat[1]),
})
}
}, 1000)
}
componentDidMount() {
this.setTime();
}
componentWillUnmount() {
clearInterval(this.timerId)
}
and if you are gonna update then use update lifecycle methods togather with mount lifecycle methods
I'm setting an interval on componentDidMount() and saving it on my App state.
The timer starts correctly, but I don't achieve to clear it when I want. This is the initial state:
this.initialState = {
score: 0,
finished: false,
currentQuestion: 0,
questions: [],
minutes: 1,
seconds: 15,
timer: 0
};
The action looks like this. It receives a timer (interval) as a parameter:
export function updateTimer(timer) {
return {
type: UPDATETIMER,
payload: {
timer
}
}
}
I have this action creator function on my reducers.js:
function timer(state = 0, action = {}) {
switch(action.type) {
case RESTART:
return 0;
case UPDATETIMER:
if(action.payload.timer !== undefined){
return action.payload.timer;
}else {
clearInterval(state);
return 0;
}
default:
return state;
}
}
In App.js I use it like this:
<Navbar
onUpdateTimer= {
(timer) => {
this.props.dispatch(updateTimer(timer))
}
}
/>
What I'm tryng to do is that if I the call has an argument it sets the interval, and if it doesn't it clears it.
The component that calls the action is this one:
import React from 'react';
export default class CountDown extends React.Component {
constructor(props) {
super(props);
this.startTimer = this.startTimer.bind(this);
this.countDown = this.countDown.bind(this);
}
startTimer() {
this.props.onUpdateTimer(setInterval(this.countDown, 1000));
}
countDown() {
let minutes = this.props.minutes;
let seconds = this.props.seconds - 1;
if (seconds === 0) {
if(minutes !== 0){
minutes -=1;
seconds = 59;
}else {
this.props.timeUp()
setTimeout(function(){
alert("SE HA ACABADO EL TIEMPO");
},0);
}
}
this.props.onUpdateTime(minutes, seconds);
}
render() {
return (
<div id="clockdiv">
<span className="minutes">{this.props.minutes} : </span>
<span className="seconds">{this.props.seconds}</span>
</div>
);
}
componentDidMount(){
this.startTimer();
}
}
Am I using correctly clearInterval(state) in the action creator function?
I'm pretty new to React and trying to write my first app to get a better understanding.
What I'm trying to build is a simple time tracking tool where the user can start and stop a work timer.
Here you can see the design I came up with:
If the user clicks on the "start" button the working time Timer component should update every second. If the user clicks then on the "take a break" button the timer should stop and instead the break time Timer component should start ticking.
I would like to reuse the Timer component for both working and break timer and just set different states.
I already managed to do this but I don't know if this is a nice way or if this can be improved and make it more generic?
My Tracker component looks like this:
class Tracker extends Component {
constructor(props) {
super(props);
this.state = {
workTime: 0,
breakTime: 0,
isRunning: false,
timerType: 'workTimer'
}
}
startTimer(type) {
this.setState({
isRunning: true,
timerType: type
});
this.timerInterval = setInterval(() => {
this.updateTimer()
}, 1000);
}
stopTimer() {
this.setState({
isRunning: false
});
clearInterval(this.timerInterval);
}
toggleBreak(type) {
this.setState({
timerType: type
});
if (!this.state.isRunning && this.state.timerType === 'breakTimer') {
this.startTimer('breakTimer');
} else if (this.state.isRunning && this.state.timerType === 'breakTimer') {
this.stopTimer();
this.startTimer('workTimer');
} else {
this.stopTimer();
this.startTimer('breakTimer');
}
}
updateTimer() {
let state = null;
if (this.state.timerType === 'workTimer') {
state = {
workTime: this.state.workTime + 1000
};
} else {
state = {
breakTime: this.state.breakTime + 1000
};
}
this.setState(state);
}
render() {
return (
<div className="tracker">
<Timer time={ this.state.workTime }/>
<Timer time={ this.state.breakTime }/>
<TimerControls
isRunning={ this.state.isRunning }
start={ () => this.startTimer('workTimer') }
stop={ () => this.stopTimer() }
toggleBreak={ () => this.toggleBreak('breakTimer') }
/>
</div>
);
}
}
Controls component:
class TimerControls extends Component {
constructor(props) {
super(props);
}
render() {
const {isRunning, start, stop, toggleBreak} = this.props;
return (
<div className="tracker__control">
<button onClick={ start } disabled={ isRunning }>Start</button>
<button onClick={ toggleBreak }>Break</button>
<button onClick={ stop } disabled={ !isRunning }>Stop</button>
</div>
);
}
}
Timer component:
class Timer extends Component {
constructor(props) {
super(props);
}
render() {
const { time } = this.props;
return (
<div className="tracker__timer">{ timeFormat(time) }</div>
);
}
}
Is there a way to get rid of the timerType conditions?
How do you make it so that whenever you click the start button, only then will the timer starts. Because right now, it starts at will.
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(prevState => ({
seconds: prevState.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
<br />
<button onClick={this.tick}> Start </button>
</div>
);
}
}
ReactDOM.render(<Timer />, mountNode);
What should I put in the onClick attribute?
You will need to bind 'tick' to the component in the constructor and move the code for starting the timer from 'componentDidMount' to 'tick' like so:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
this.tick = this.tick.bind(this); // bind to the component
}
tick() {
// start timer after button is clicked
this.interval = setInterval(() => {
this.setState(prevState => ({
seconds: prevState.seconds + 1
}));
}, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
<br />
<button onClick={this.tick}> Start </button>
</div>
);
}
}
ReactDOM.render(<Timer />, mountNode);
Hope that helps.
This is how we can achieve the same using React Hooks.
const Timer = () => {
const [isActive, setIsActive] = useState(false);
const [seconds, setSeconds] = useState(0);
useEffect(() => {
let timer = null;
if(isActive){
timer = setInterval(() => {
setSeconds((seconds) => seconds + 1);
}, 1000);
}
return () => {
clearInterval(timer);
};
});
return (
<div>
Seconds: {seconds}
<br />
<button onClick={()=>{
setIsActive(true);
}}> Start </button>
</div>
);
};