Реагировать, почему мой clearInterval не может остановить setInterval?

#reactjs

#reactjs

Вопрос:

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

Я пытаюсь прекратить передачу новых реквизитов дочернему компоненту, когда щелкаю по контейнеру flexbox. Я написал функцию handleClick и setInterval() или clearInterval() на основе переменной обновления в состоянии.

Что я хочу, так это когда я нажимаю на любой из часов / минут / секунд, поле выбора изменится на поле ввода, и таймер остановится. Как только я нажму enter, он вернется к таймеру.

 class Timer extends React.Component{
    constructor(props){
        super (props);
        this.timerRef = React.createRef();
        this.state = {
            hh: new Date().getHours().toString().padStart(2,"0"),
            mm: new Date().getMinutes().toString().padStart(2,"0"),
            ss: new Date().getSeconds().toString().padStart(2,"0"),
            suffix: new Date().getHours() > 12 ? "PM" : "AM",
            update:true,
        };
    }

    tick() {
        this.setState({
            hh: new Date().getHours().toString().padStart(2,"0"),
            mm: new Date().getMinutes().toString().padStart(2,"0"),
            ss: new Date().getSeconds().toString().padStart(2,"0"),
            suffix: new Date().getHours() > 12 ? "PM" : "AM",
        })
    }


    componentDidMount(){
        this.intervalId = setInterval(
            () => this.tick(), 1000
        );
    }
    
    componentWillUnmount(){
        clearInterval(this.intervalId);
    }

    handleClick(){       
        this.setState(state => ({update: !state.update}));
        console.log("1",this.state.update);
        if (this.state.update){
            this.intervalId = setInterval(
                () => this.tick(), 1000
            );
            console.log("2 set interval",this.intervalId);
        }
        else{
            clearInterval(this.intervalId);
            console.log("2 clear interval");
        }
    }

    render() {
        const { hh, mm, ss, suffix } = this.state;
        return (
          <div className="box" > London Clock
              <div className="flexbox-container" onClick={() => this.handleClick()}>
                <Content time={hh}></Content>
                <div><p>:</p></div>
                <Content time={mm}></Content>
                <div><p>:</p></div>
                <Content time={ss}></Content>
                <div className="suffix"><p>{suffix}</p></div>
              </div>
          </div>
        );
    }
}

class Content extends React.Component {
    state = {
        editMode: false,
        time: ""
    };

    componentDidMount(){
        this.setState({
            time: this.props.time,
        })
    }
    
    
    handleKeyDown(event){
        console.log(event,event.key === 'Enter');
        if (event.key === 'Enter'){
            this.setState({
                editMode: false,
            })
        } 
    }
    
    render() {
        const {editMode} = this.state;
        return (
            <div> 
              {editMode? (
                    <p>
                        <input
                        defaultValue={this.props.time}
                        onKeyPress={e => this.handleKeyDown(e)}
                    />
                    </p>
              ) : (
                    <p onClick={() => this.setState({ editMode: true })}>{this.props.time}</p>
              )}
            </div>
          );
    }
}

ReactDOM.render(
    <Timer/>,
    document.body
  );  
 .flexbox-container {
    display: flex;
    flex-direction: row;
}

.suffix{
    padding-left: 20px;
}

.box{
    border-style: solid;
    padding: 10px;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>  

Ответ №1:

Когда ваш компонент смонтирован, он запустит интервал и назначит его intervalId .

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

Либо передайте обратный вызов в setState(updater, [callback]) , либо переместите свою интервальную логику в componentDidUpdate , что позволит вам отменить дублирование интервальной логики

Комментарии:

1. Спасибо, это хорошо объяснено и действительно имеет смысл. Оба метода работают в моей ситуации.

Ответ №2:

Попробуйте, как показано ниже, вместо настройки состояния и привязки вашей tick функции.

        componentDidMount(){
            this.intervalId = setInterval(
                () => this.tick.bind(this), 1000
            );
        }
        
        componentWillUnmount(){
            clearInterval(this.intervalId);
        }
  

Комментарии:

1. Это не работает. Я чувствую, что проблема в функции handleClick, даже если я вижу, что программа выполняет clearInterval внутри handleClick, она все еще продолжает обновлять дочерние компоненты.

Ответ №3:

Я думаю, вы можете использовать Ref React вместо состояния.

 constructor(props) {
    this.timerRef = React.createRef();
  }

  componentDidMount() {
    this.timerRef.current = setInterval(this.tick, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerRef.current);
  }
  

Комментарии:

1. Спасибо за ответ, но моя настоящая проблема в том, что я пытаюсь clearInterval в handleClick() на основе его состояния, и это не сработает. componentDidMount() и componentWillUnmount() выглядят нормально.

2. Да, вы также можете использовать этот подход в handleClick. У меня была такая же проблема в моем прошлом проекте, и я решил проблему таким образом. Я думаю, вам следует сохранить идентификатор интервала в ref вместо состояния

3. Я думаю, что использовать ссылку работает, и @dube для меня проще. Спасибо за ваш ответ!