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>
);
};
Related
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>
)
}
}
Hi im trying to code a Timer. My problem is that the countdown should start after clicking a button and when that status is changed from start to stop and vise versa. I cant figure out how to handle it and which level to put it.
ADDITIONAL INFO:
When clicking the button, it goes to the method handler. Changes the status with setstate() but it renders at the end. Which is to late for the countdown to start.
Here is the Game Component:
import React, { Component } from 'react';
import './Game.css';
import Timer from './Timer'
class Game extends Component {
constructor() {
super();
}
state = {
buttonStatus: {status:"Start" , classButton:"Button ButtonBackgroundColorGrey" },
dotclass : "",
timer: 60
}
componentDidMount() {
this.timersignal();
}
buttonclick = () =>{
(this.state.buttonStatus.status === "Start")
? this.setState({buttonStatus:{status:"Stop",classButton:"Button ButtonBackgroundColorRed"},dotclass:"Dot"})
: this.setState({buttonStatus:{status:"Start",classButton:"Button ButtonBackgroundColorGrey"},dotclass:""})
this.componentDidMount();
}
timersignal = () => {
if(this.state.buttonStatus.status === "Stop") {
this.Interval = setInterval(() =>{
this.setState(() => ({
timer : this.state.timer - 1
}))
},1000)
console.log("didMount start")
}
else(
console.log("didMount stop")
)
}
render() {
return (
<div>
<div className="Body-Container">
<h2 className="States"> Time </h2>
<Timer buttonstate= {this.state.timer}/>
<button className={this.state.buttonStatus.classButton} onClick={this.buttonclick}>{this.state.buttonStatus.status}</button>
</div>
</div>
);
}
}
export default Game;
Here is the Timer Component:
import React, { Component } from 'react';
import "./Timer.css";
class Timer extends Component {
render() {
return (
<h3 className="Timer">{this.props.buttonstate}</h3>
);
}
}
export default Timer ;
You just need one method and call it in componentDidMount and on click.
timerToggle = () =>{
if((this.state.buttonStatus.status === "Start") {
this.setState({buttonStatus:{status:"Stop",classButton:"Button ButtonBackgroundColorRed"},dotclass:"Dot"})
clearInterval(this.Interval);
}else{
this.setState({buttonStatus:{status:"Start",classButton:"Button ButtonBackgroundColorGrey"},dotclass:""}) ;
this.Interval = setInterval(() =>{
this.setState(() => ({
timer : this.state.timer - 1
}))
},1000)
}
}
componentDidMount() {
this.timerToggle();
}
The Final Answer:
timerToggle = () =>{
if(this.state.buttonStatus.status === "Start") {
this.setState({buttonStatus:{status:"Stop",classButton:"Button ButtonBackgroundColorRed"},dotclass:"Dot"})
this.Interval = setInterval(() =>{
this.setState(() => ({
timer : this.state.timer - 1
}))
},1000)
}
else{
this.setState({buttonStatus:{status:"Start",classButton:"Button ButtonBackgroundColorGrey"},dotclass:""}) ;
clearInterval(this.Interval);
}
}
I was learning about various lifecycle components, so I have a counter in my code, which can be increased or decreased using buttons and I have a seed generator which should be called on button click and it should change the value of the counter to seed, I have added function for the seed to be set to Number.parseInt(Math.random() * 100)
when I run the code, it gives warning in chrome,
also when I click on increment , the counter is set to () => this.setState({ seed: Number.parseInt(Math.random() * 100) })1 , and when I press decrement(click) the counter is set to NaN.
There was a similar question related to this warning but that code was not related to mine.
APP COMPONENT
import React from "react";
import Counter from "./Counter";
import "./App.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
mount: true,
ignoreProp: 0,
seed: 40
};
this.mountCounter = () => this.setState({ mount: true });
this.unmountCounter = () => this.setState({ mount: false });
this.ignoreProp = () => this.setState({ ignoreProp: Math.random() });
this.seedGenerator = () =>
this.setState({ seed: Number.parseInt(Math.random() * 100) });
}
render() {
let counter = null;
if (this.state.mount) {
counter = (
<Counter ignoreProp={this.state.ignoreProp} seed={this.seedGenerator} />
);
}
return (
<div className="App">
<button onClick={this.mountCounter} disabled={this.state.mount}>
Mount Counter
</button>
<button onClick={this.unmountCounter} disabled={!this.state.mount}>
Unmount Counter
</button>
<button onClick={this.ignoreProp}>Ignore Prop</button>
<button onClick={this.seedGenerator}>Generate seed</button>
{counter}
</div>
);
}
}
export default App;
COUNTER COMPONENT
import React from "react";
class Counter extends React.Component {
constructor(props) {
console.log("Constructor");
super(props);
this.state = {
counter: 0,
seed: 0
};
this.increment = () => this.setState({ counter: this.state.counter + 1 });
this.decrement = () => this.setState({ counter: this.state.counter - 1 });
}
static getDerivedStateFromProps(props, state) {
if (props.seed && state.seed !== props.seed) {
return {
seed: props.seed,
counter: props.seed
};
}
return null;
}
componentDidMount() {
console.log("Component Did Mount");
console.log("-------------------");
}
shouldComponentUpdate(nextProps, nextState) {
if (
nextProps.ignoreProp &&
this.props.ignoreProp !== nextProps.ignoreProp
) {
console.log("Should Component Update- DO NOT RENDER");
return false;
}
console.log("Should Component Update- RENDER");
return true;
}
render() {
console.log("Render");
return (
<div>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
<div className="counter">Counter: {this.state.counter}</div>
</div>
);
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("Component Did Update");
console.log("--------------------");
}
componentWillUnmount() {
console.log("Component Will Unmount");
console.log("----------------------");
}
}
export default Counter;
You pass seedGenerator, a function, as the seed prop down to Counter, and since you have
return {
seed: props.seed,
counter: props.seed
}
in getDerivedStateFromProps (likely a copy-paste typo?), the
Counter: {this.state.counter}
render fragment tries to render seedGenerator, a function.
constructor(props) {
super(props);
this.state = {
count: 20
};
}
const {count} = this.state
componentDidMount (){
const {startCount} = this.props
this.setState({
count: startCount
})
this.doIntervalChange()
}
componentDidMount (){
this.myInterval = setInterval(() => {
this.setState(prevState => ({
count: prevState.count - 1
}))
},1000)
}
componentWillMount (){
clearInterval(this.myInterval)
}
Try this (untested):
this.myInterval = setInterval(() => {
this.setState(prevState => {
const newCount = prevState.count - 1;
if (newCount == 0) {
clearInterval(this.myInterval);
}
return { count: newCount };
});
}, 1000);
constructor (props){
super(props)
this.state = {
count: 20
}
}
render() {
const {count} = this.state
return (
<div>
<Container className="comming">
<Form className="boxrandomtest">
<h1 className="textrandomtest">แบบทดสอบวัดระดับ</h1> <h1 className="cdtimer"><FontAwesomeIcon icon="clock" /> {count}</h1>
<Form className="Q1">
<h3>1. เวลาใดที่กินเวลาน้อยที่สุด</h3> <br></br>
<Button className="rdTest1" href='/randomtest2'><h5>ก.อดีต</h5></Button><br></br>
<Button className="rdTest1" href='/randomtest13'><h5>ข.ปัจจุบัน</h5></Button><br></br>
<Button className="rdTest1" href='/randomtest2'><h5>ค.อนาคต</h5></Button><br></br>
</Form>
<Button className="rdTestbt1" href='/randomtest1'><h5>รีหน้าเดิม</h5></Button>
<Button className="rdTestbt2" href='/randomtest2'><h5>ข้อต่อไป</h5></Button>
</Form>
</Container>
</div>
)
}
componentDidMount (){
const {startCount} = this.props
this.setState({
count: startCount
})
this.doIntervalChange()
}
componentDidMount (){
this.myInterval = setInterval(() => {
this.setState(prevState => {
const newCount = prevState.count - 1;
if (newCount == 0) {
clearInterval(this.myInterval);
}
return { count: newCount };
});
}, 1000);
}
componentWillMount (){
clearInterval(this.myInterval)
}
}
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?