Изменение входа сбрасывает таймер

Привет, ребята, я начал учиться реагировать недавно, и у меня есть некоторые проблемы. Я пытаюсь сделать простое приложение реакции, один из компонентов, которые я делаю, это секундомер.
У меня проблема в том, что когда я начинаю вводить данные для секундомера, таймер сбрасывается.

Вот мой основной компонент:

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;

и мой компонент секундомер:

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;

Вот gif проблемы https://imgur.com/9xqMW96
Как вы можете видеть, мой таймер сбрасывается после того, как я начал вводить данные. Я хотел бы для него сбросить только когда пользователь нажимает enter ключ или использует submit кнопка.

Я пытался сделать что-то вроде этого:

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

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

но это не сработало для меня. Вы можете увидеть код в действии здесь: https://karadjordje.github.io/countdown-stopwatch-react/

2 ответа

Решение

Я обновил свой код.

Вместо того, чтобы использовать componentWillUpdate я использую componentDidUpdate и это мой код для этого:

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');
    }
}

В основном я только обновляю таймер, если предыдущий таймер отличается от текущего.

Вы останавливаете interval на каждой новой опоре, которую получает компонент.
Вы можете обрабатывать время в местном state или явно передать правильные новые значения от родителя.

Я сделал небольшой базовый пример, чтобы вы могли видеть поток данных и то, как каждое событие отвечает за небольшую часть данных.
Надеюсь, это поможет.

class App extends React.Component {

  state = {
    startTime: 5,
    currentTime: 5,
    textInput: ''
  }

  startTimer = () => {
    if (this.interval) {
      clearInterval(this.interval);
    }
    this.interval = setInterval(() => {
      this.setState(prev => {
        if (prev.currentTime === 0) {
          this.stopTimer();
          return { ...prev, currentTime: prev.startTime };
        } else {
          return {
            ...prev,
            currentTime: prev.currentTime - 1
          }
        }
      })
    }, 1000)
  }

  stopTimer = () => {
    clearInterval(this.interval);
  }

  updateInput = ({ target }) => {
    this.setState(prev => ({ textInput: target.value }));
  }

  setStartTime = () => {
    this.stopTimer();
    this.setState(({ textInput }) => ({ startTime: textInput, currentTime: textInput, textInput: '' }));
  }

  render() {
    const { currentTime, textInput } = this.state;
    return (
      <div >
        <div>{currentTime}</div>
        <button onClick={this.startTimer}>Start timer</button>
        <div>
          <input placeholder="Enter start time" value={textInput} onChange={this.updateInput} />
          <button onClick={this.setStartTime}>Set Start time</button>
        </div>
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>

Другие вопросы по тегам