Search code examples
reactjssetstatereact-propsreact-component

Input change resets timer


Hello guys I started learning react recently and I'm having some issues. I'm trying to make simple react app, one of the components I'm making is stopwatch.
The issue I'm having is that when I start typing in my input for stopwatch the timer resets.

Here is my main component:

import React, { Component } from 'react';
import Clock from '../components/Clock.jsx';
import Stopwatch from '../components/Stopwatch.jsx';
import '../css/App.css';
import { Form, FormControl, Button } from 'react-bootstrap';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            deadline: 'December 25, 2018',
            newDeadline: '',
            timer: 60,
            newTimer: '',
        };
    }

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

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

    render() {
        return (
            <div className='app'>
                <div className='app_title'>
                    Countdown to {this.state.deadline}
                </div>
                <Clock
                    deadline = {this.state.deadline}
                />
                <Form inline >
                    <FormControl
                        className="deadline_input"
                        type="text"
                        placeholder="New date"
                        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="stopwatch_title">
                    Stopwatch from {this.state.timer} seconds
                </div>

                <Form inline>
                    <FormControl
                        className="stopwatch_input"
                        type="text"
                        placeholder="Enter time"
                        onChange={event => this.setState({newTimer: event.target.value})}
                        onKeyPress={event => {
                            if (event.key === 'Enter') {
                                event.preventDefault();
                                this.changeTimer();
                            }
                        }}
                    />
                    <Button onClick={() => this.changeTimer()} >
                        Submit
                    </Button>
                </Form>

                <Stopwatch
                    timer = {this.state.timer}
                />

            </div>
        );
    }
}

export default App;

and my stopwatch component:

import React, {Component} from  'react';
import '../css/App.css';
import { Button } from 'react-bootstrap';


class Stopwatch extends Component {
    constructor(props) {
        super(props);
        this.state = {
            stopwatch: props.timer,
        };
        this.decrementer = null;
    }

    componentWillReceiveProps(nextProps) {
        clearInterval(this.decrementer);
        this.timerCountdown(nextProps.timer);
    }

    timerCountdown(newTimer) {

        // First we update our stopwatch with new timer
        this.setState({
            stopwatch: newTimer
        });

    }

    startTimer() {
        // Then we decrement stopwatch by 1 every second
        this.decrementer = setInterval( () => {
            this.setState({
                stopwatch: this.state.stopwatch -1,
            });
        },1000);
    }

    componentDidUpdate() {
        if (this.state.stopwatch < 1) {
            clearInterval(this.decrementer);
            alert('Countdown finished');
        }
    }

    render() {
        return(
            <div>
                <Button onClick={() => this.startTimer()} >
                    Start
                </Button>
                <div className="stopwatch"> {this.state.stopwatch} </div>
            </div>
        );
    }
}

export default Stopwatch;

Here is gif of the problem https://i.sstatic.net/Hz1kN.jpg
As you can see my timer resets after I start typing in input. I would like for it to reset only when user presses enter key or uses submit button.

I've tried doing something like this:

  <input value={this.state.newTimer} onChange={evt => this.updateInputValue(evt)}/>

  updateInputValue: function(evt) {
    this.setState({
      newTimer: evt.target.value
    });
  }

but it didn't work for me. You can see code in action here: https://karadjordje.github.io/countdown-stopwatch-react/


Solution

  • I have updated my code.

    Instead of using componentWillUpdate I'm using componentDidUpdate and this is my code for it:

    componentDidUpdate(prevProps) {
        console.log('componentDidUpdate', this.props, prevProps);
        if (prevProps.timer !== this.props.timer) {
            this.updateTimer(this.props.timer);
            clearInterval(this.decrementer);
        }
    
        if (this.state.stopwatch < 1) {
            clearInterval(this.decrementer);
            alert('Countdown finished');
        }
    }
    

    Basically I'm only updating timer is previous timer is different from current.