Search code examples
javascriptreactjscountdown

React stopwatch


Hello guys I started learning react recently and I'm having some problems. I'm trying to make simple react app, one of the components I'm making is stopwatch. Now I'm having problem using props that I passed to my Stopwatch component from my parent component. This is my app component:

import React, {Component} from 'react';
import { Form,FormControl, Button} from 'react-bootstrap';

// Compoenents
import Clock from './Clock';
import Stopwatch from './Stopwatch';

// CSS
import './css/app.css';

class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            deadline: 'December 31, 2017',
            newDeadline: '',
            timer: 60,
            newTimer: ''
        }
    }

    changeDeadline () {
        this.setState({deadline: this.state.newDeadline});
    }

    checkTimer() {
        this.setState({timer: this.state.newTimer});
    }

    render() {
        return (
            <div className='app'>
                <div className='appTitle'>
                    Countdown to {this.state.deadline}
                </div>
                <Clock 
                    deadline={this.state.deadline} // This is how we send props to our child component
                />
                <Form inline={true} >
                    <FormControl 
                        className='deadlineInput'
                        type="text" 
                        placeholder='Write date to check' 
                        onChange={event => this.setState({newDeadline: event.target.value})}
                        onKeyPress={event =>  {
                            if(event.key === 'Enter') {
                                event.preventDefault();
                                this.changeDeadline();;
                            }
                        }}
                    />
                    <Button 
                        onClick={() => this.changeDeadline()} 
                    >
                        Submit
                    </Button>
                </Form>

                <div className='stopwatchTitle'>
                    Use stopwatch to {this.state.timer} seconds
                </div>
                <Stopwatch 
                    timer={this.state.timer} // This is how we send props to our child component
                />
                <Form inline={true} >
                    <FormControl 
                        className='timerInput'
                        type="text" 
                        placeholder='Set your timer' 
                        onChange={event => this.setState({newTimer: event.target.value})}
                        onKeyPress={event =>  {
                            if(event.key === 'Enter') {
                                event.preventDefault();
                                this.checkTimer();;
                            }
                        }}
                    />
                    <Button 
                        onClick={() => this.checkTimer()} 
                    >
                        Start
                    </Button>
                </Form>
            </div>
        )
    }
}

export default App;

and this is my Stopwatch component:

import React, {Component} from 'react';


// CSS
import './css/stopwatch.css';

class Stopwatch extends Component {

    constructor(props) {
        super(props);
        this.state = {
            stopWatch: 0,
        }
        this.decrementer = null;
    }

    // This function runs before component completely renders on the application (otherwise we might create infinite loop)
    componentWillMount() {
        this.startTimer(this.props.timer);
    }

    startTimer(timer) {

        let stopWatch = timer;
        console.log(stopWatch)

        this.decrementer = setInterval( () =>
            this.setState({
            stopWatch: this.state.stopWatch - 1
        })
        , 1000);

    }

    render() {
        return (
            <div>
                <div className='myStopwatch'> {this.state.stopWatch} seconds</div>
            </div>
        )
    }

}

export default Stopwatch;

Right now App always starts counting from 0 and goes into minus. How can I use my timer props that I pass down from my parent to my child component ? I want my stopwatch starting time be equal to my timer props. Also how to make countdown stop when it reaches 0 ?


Solution

  • How can I use my timer props that I pass down from my parent to my child component ? I want my stopwatch starting time be equal to my timer props.

    On startTimer(timer) you pass the timer to the stopWatch variable, but you end up using this.state.stopWatch to initialize your interval. Since this.state.stopWatch is always 0, you stopwatch will always begin on 0. One way to achieve what you want is to initialize this.state.stopWatch with the value you are receiving from props:

    constructor(props) {
        super(props);
        this.state = {
            stopWatch: props.timer
        }
        this.decrementer = null;
    }
    
    componentWillMount() {
        this.startTimer(); // now you dont need to pass timer because is already in your local state
    }
    

    Also how to make countdown stop when it reaches 0 ?

    To achieve this you need to clear the interval once the timer reaches 0. You might also want to check if stopWatch is 0 to prevent starting the interval:

    startTimer() {
    
    
        if(!this.state.stopWatch) return; // check if stopWatch is 0
    
        this.decrementer = setInterval( () => {
            const stopWatch = this.state.stopWatch - 1;        
    
            this.setState({ stopWatch });
    
            if(stopWatch < 1) clearInterval(this.decrementer); // clears interval one stopWatch reaches 0
    
        }, 1000);
    }