Перезапуск setInterval после clearInterval в React

В этой игре, которую я создаю, я очищаю setInterval после того, как пользователь проигрывает. Когда они нажимают "Играть снова"? Я хочу, чтобы таймер снова запустился, но у меня возникают проблемы с этим при использовании React. Я пробовал несколько вещей, таких как разделение таймера на отдельный компонент, создание вспомогательной функции и использование методов жизненного цикла, но я просто не могу понять эту часть. Я могу запустить и очистить setInterval нормально, но он перезапускает его, вот и моя проблема.

import React, {Component} from 'react';
// helper function to set a random action
function setRandomAction() {
    let actions = ['bop it', 'pull it', 'twist it'];
    let rando = actions[Math.floor(Math.random() * actions.length)];
    return rando;
}
class BopIt extends Component {
    constructor(props) {
        super(props);
        // set initial action in this.state so it is not empty on pageload
        this.state = {
            action: setRandomAction(),
            countdown: 3,
            userPressed: '',
            play: true
        }
        this.bind = this.keyPressed.bind(this);
        this.bind = this.keepPlaying.bind(this);
        this.bind = this.timer.bind(this);
        this.bind = this.startTimer.bind(this);
        this.bind = this.endGame.bind(this);
        this.quitGame = this.quitGame.bind(this);
        this.playAgain = this.playAgain.bind(this);
    }
    componentDidMount() {
        this.keyPressed();
        this.startTimer();
    }
    startTimer() {
        let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
        this.stopIntervalId = setInterval(() => this.timer(), setTimerTime);
    }
    componentWillUnmount() {
        this.startTimer();
        this.keyPressed();
        this.keepPlaying();
        this.endGame();
    }

    timer() {
        var count = this.state.countdown;
        if (count === 0) {
            count = 4
        }
        this.setState({countdown: count - 1});
    }
    keyPressed() {
        document.addEventListener('keyup', (e) => {
            if (e.key === 'ArrowLeft') {
                this.setState({
                    userPressed: 'pull it'
                });
            } else if (e.key === 'ArrowDown') {
                this.setState({
                    userPressed: 'bop it'
                });
            } else if (e.key === 'ArrowRight') {
                this.setState({
                    userPressed: 'twist it'
                });
            } else {
                // this.endGame();
                this.setState({
                    userPressed: 'wrong'
                });
            }
            if (this.state.userPressed !== this.state.action) {
                this.endGame();
            } else {
                this.keepPlaying();
            }
        });
    }
    keepPlaying() {
        let actions = ['bop it', 'pull it', 'twist it'];
        let rando = actions[Math.floor(Math.random() * actions.length)];
        this.setState({
            action: rando,
            userPressed: ''
        });
    }
    endGame() {
        console.info('You Lost!!!');
        this.setState({
            play: false
        });
    }
    quitGame() {
        clearInterval(this.stopIntervalId);
    }
    playAgain() {
        this.setState({
            play: true,
            action: setRandomAction(),
            countdown: 3
        });
    }
    render() {
        // if (this.state.countdown <= 0) {
        //   this.endGame();
        // }
        console.info(this.state)
        let gameAction = `${this.state.action} ${this.state.countdown}`;
        return (
          <div className = "bop-it">
            <div className = "show-action">
            {this.state.play ?  gameAction : <ResetGame playAgain = {this.playAgain} quitGame = {this.quitGame}/> }
            </div>
            <span>Pull It</span>
              <br/>
            <span>Bop It</span>
              <br/>
            <span>Twist It</span>
          </div>
        );
    }
}
class ResetGame extends Component {
    render() {
        return (
          <div>
            <input type = "button" value = "Play Again?" onClick = {this.props.playAgain}/>
            <input type = "button" value = "Quit Game?" onClick = {this.props.quitGame}/>
          </div>
        );
    }
}
export default BopIt

Обновлено:

В итоге я просто вызвал this.startTimer() в конце метода playAgain(). Я мог бы поклясться, что делал это раньше, но, видимо, нет. Я также стараюсь вызывать clearInterval только в одном месте, чтобы не было конфликтов в других частях приложения. Другая проблема, с которой я столкнулся, заключалась в том, что всякий раз, когда я перезапускал setInterval, таймер отсчитывал быстрее. Это было из-за этого: let setTimerTime = parseInt(`${this.state.countdown - 2}000`); this.stopIntervalId = setInterval(() => this.timer(), setTimerTime); Я поместил туда эту строку кода, потому что в конечном итоге хочу, чтобы пользователь сам выбирал скорость игры, но это полностью испортило свойство countdown в объекте состояния моего приложения. Удаление этой строки на данный момент также устранило некоторую путаницу.

import React, {Component} from 'react';
// helper function to set a random action
function setRandomAction() {
  let actions = ['bop it', 'pull it', 'twist it'];
  let rando = actions[Math.floor(Math.random() * actions.length)];

  return rando;
}

class BopIt extends Component {
  constructor(props) {
    super(props);
    // set initial action in this.state so it is not empty on pageload
    this.state = {
      action: setRandomAction(),
      countdown: 3,
      userPressed: '',
      play: true
    }
    this.bind = this.keyPressed.bind(this);
    this.bind = this.keepPlaying.bind(this);
    this.bind = this.endGame.bind(this);
    this.bind = this.timer.bind(this);
    this.bind = this.startTimer.bind(this);
    this.quitGame = this.quitGame.bind(this);
    this.playAgain = this.playAgain.bind(this);
  }
  componentDidMount() {
    this.keyPressed();
    this.startTimer();
  }
  startTimer() {
    // let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
    this.stopIntervalId = setInterval(() => this.timer(), 1000);
  }
  componentWillUnmount() {
    this.keyPressed();
    this.keepPlaying();
    this.endGame();
  }
  timer() {
    let count = this.state.countdown;
    if (count === 0) {
      count = 4
      // end the game if the timer hits 0
      this.endGame();
    }
    this.setState({countdown: count - 1});
  }
  keyPressed() {
    document.addEventListener('keyup', (e) => {
      if (e.key === 'ArrowLeft') {
        this.setState({
          userPressed: 'pull it'
        });
      } else if (e.key === 'ArrowDown') {
        this.setState({
          userPressed: 'bop it'
        });
      } else if (e.key === 'ArrowRight') {
        this.setState({
          userPressed: 'twist it'
        });
      } else {
        this.setState({
          userPressed: 'wrong'
        });
      }
      // if user presses wrong key, then the game is over
      if (this.state.userPressed !== this.state.action) {
        this.endGame();
      } else {
        // otherwise, reset the time and chooose a random action
        this.keepPlaying();
      }
    });
  }

  keepPlaying() {
    this.setState({
      action: setRandomAction(),
      countdown: 3,
      userPressed: ''
    });
  }
  endGame() {
    console.info('You Lost!!!');
    this.setState({
      play: false
    });
    clearInterval(this.stopIntervalId);
  }
  quitGame() {
    // clearInterval(this.stopIntervalId);
    console.info('you have left the game')
  }
  playAgain() {
    this.setState({
      play: true,
      action: setRandomAction(),
      countdown: 3
    });
    this.startTimer();
  }
  render() {
    let gameAction = `${this.state.action} ${this.state.countdown}`;
    return (
      <div className = "bop-it">
        <div className = "show-action">
        {this.state.play ?  gameAction :
          <ResetGame
            playAgain = {this.playAgain}
            quitGame = {this.quitGame}
          />
        }
        </div>
        <span>Pull It</span>
          <br/>
        <span>Bop It</span>
          <br/>
        <span>Twist It</span>
      </div>
    );
  }
}


class ResetGame extends Component {
  render() {
    return (
      <div>
        <input type = "button" value = "Play Again?" onClick = {this.props.playAgain}/>
        <input type = "button" value = "Quit Game?" onClick = {this.props.quitGame}/>
      </div>
    );
  }
}

export default BopIt
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
940
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, проблема может заключаться в том, что вы одновременно устанавливаете 2 таймера, один в componentWillUnmount, а другой в componentWillMount, и когда вы их удаляете, вы удаляете только один из них, потому что другой теряется при перезаписи переменной

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

startTimer() {
    let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
    if (!this.stopIntervalId) {
        this.stopIntervalId = setInterval(() => this.timer(), setTimerTime);
    }
}
quitGame() {
    clearInterval(this.stopIntervalId);
    this.stopIntervalId = undefined;
}
playAgain() {
    this.setState({
        play: true,
        action: setRandomAction(),
        countdown: 3
    });
    this.startTimer()
}

Я понимаю, о чем вы говорите, и это определенно могло быть проблемой. Я пока не нашел хороших примеров, объясняющих методы жизненного цикла React (которые имеют для меня смысл), поэтому знание того, как использовать componentWillUnmount и componentWillMount, все еще то, чем я занимаюсь. Вы можете увидеть мою правку выше, чтобы узнать, как я решил проблему.

Jawaka72 10.09.2018 01:58

В своем исходном ответе я также добавил вызов startTimer() в конце метода playAgain (я сказал, что методы, которые я добавил в вопрос, были теми, которые я бы изменил, потому что в них отсутствовала логика или информация), это будет приятно, что вы дали мне за это несколько голосов, потому что это тоже помогло бы вам.

Alejandro Vales 10.09.2018 10:43

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