Передача состояния с помощью .затем функция и приятное оповещение

#javascript #reactjs #state #react-props #sweetalert

#javascript #reactjs #состояние #реагировать-реквизит #sweetalert

Вопрос:

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

Чего я хочу добиться, так это того, что при нажатии кнопки «просмотреть окончательный результат» я хочу, чтобы пользователь был перенаправлен на страницу завершения игры, где будет показан окончательный результат.

Я очень новичок в react / программировании в целом, и я попробовал несколько вариантов безрезультатно. Используемый текущий метод дает мне:

эта ошибка

поэтому любая помощь или направление были бы с благодарностью приняты 🙂

 export class QuestionModal extends Component {

  constructor(props) {
    super(props);
    
    this.state ={
      questions: {},
      answerOptions: [],
      chosenAnswer: '',
      isAnswerCorrect: false,
      value: '',
      answer: '',
      gameOver: false,
      gameScore: this.props.context.gameScore,
    
    };
    this.handleChange = this.handleChange.bind(this);
    this.gameOver = this.gameOver.bind(this);
  }

  // Axios call to get questions from database
  componentDidMount() {
    axios.get('/hacker')
        .then((response) => {
          console.log([response.data]);
          const result = response.data;
          this.setState({
            questions: result,
            answerOptions: result.answerOptions,
            answer: result.answer,
            explanation: result.explanation,
          }); console.log(this.state.answer);
        }).catch((error) => {
          this.setState({isLoaded: true,
            error});
        });
  }

  /* This function is invoked when the user hits submit after answering the question. It does 2 things: 

  1. Compares the answer provided by the user vs the answer in the db. Sweetalert is then used to pop up and show the correct/wrong answer.
  2. Invokes the onHide function to hide the question modal
  
  */

  checkAnswer() {
    console.log(this.state.gameScore);
    if (this.state.answerOptions[this.state.answer] === this.state.value) {
      console.log('correct');
      swal("Nice one!", this.state.explanation, "success",
        {
            closeOnClickOutside: false, 
            buttons: {
                confirm: "Back to the game!"
            }
        }); 
        
    } else {
      console.log('incorrect');
      swal("So close!", this.state.explanation, "error",
        {
          closeOnClickOutside: false, 
          buttons: {
              confirm: "View your final score!"
          },
      }).then (function() {
      // window.location.href = "./gameover";
     this.gameOver();
        });
      }
    this.props.onHide();
  }

   gameOver() {
    console.log (this.state.gameScore)
    this.props.history.push({
        pathname: `/gameover`,
        gameScore: this.state.gameScore,
    });
  }

  handleChange(event) {
    console.log('value is '   event.target.value);
    this.setState({
        value: event.target.value,
        gameScore: this.props.context.gameScore,
    });
  }

  render() {
        return (
        <Modal
            {...this.props}
            size="lg"
            aria-labelledby="contained-modal-title-vcenter"
            centered
            backdrop="static"
            keyboard={false}
        >
            <Modal.Header>
            <Modal.Title className="hackerGameModal"> Answer the question below correctly to continue with the game!</Modal.Title>
            </Modal.Header>
            <Modal.Body>
            <div className="hackerGameQuestion">
                <h4>{this.state.questions.question}</h4>
                <form>
                <FormControl component="fieldset" >
                    <RadioGroup aria-label="quiz" name="hackerGameAnswers" onChange={this.handleChange} value={this.state.value}>
                    {this.state.answerOptions.map((option) => {
                        return (
                        < FormControlLabel key={option} value={option} control={<Radio color="primary"/>} label={option}/>
                        );
                    })}
                    </RadioGroup>
                </FormControl>
                </form>
            </div>
            </Modal.Body>
            <Modal.Footer>
            <Button variant="contained" type="submit" color="primary" onClick={this.checkAnswer.bind(this)}>Submit</Button>
            </Modal.Footer>
        </Modal>
        );
    }
  
}

export default withRouter(QuestionModal);
  

Редактировать: Console.log(this.state.GameScore) в функции checkAnswer() показывает текущий результат. Ошибка возникает в строке 83, где:

 .then (function() {
     this.gameOver() //this is line 83
  

Но при вызове функции GameOver() появляется неопределенная ошибка.

    gameOver() {
    console.log (this.state.gameScore)
    this.props.history.push({
        pathname: `/gameover`,
        gameScore: this.state.gameScore,
    });
  }
  

Hacker.js (основной файл игры, некоторые функции были удалены, поскольку я не думаю, что это имеет отношение к проблеме)

 class Hacker extends Component {
  constructor(props, context) {
    super(props, context);

    this.state = {
      display: 'none',
      gameStarted: false,
      hackerHasBeenHit: false,
      showChallengeQuestion: false,
      gameScore: 0,
      buttonDisplay: 'none',
      showQuizModal: false,
    };
  }

  /* This function is invoked when the user clicks the start button, the interval sets the speed to which the hacker appears */

  startGame() {
    if (this.state.gameStarted) {
      return;
    }

    this.setState({
      gameStarted: true,
      display: 'block',
    });
    setInterval(() => {
      this.showHackers();
    }, 1000);
  }


  /* This function does 2 things:
          1. When the user clicks on the "hacker", the image will change to the shield for a certain period of time
          2. Add point to score
  */

  countScore(e) {
    const target = e.target;
    target.parentNode.classList.add('gameShield');
    this.setState({
      hackerHasBeenHit: true,
      gameScore: this.state.gameScore   1,
    });
    window.setTimeout(function() {
      target.parentNode.classList.remove('gameShield');
    }, 1000); // This sets the time which the shield is displayed for
    this.challengeQuestion();
  }


  /* This function shows the challenge question every 5 times

        1 is added to challengeScore as gameScore starts from 0. Ensures question does not pop up at 0, 6, 11, etc

  */

  challengeQuestion() {
    const challengeScore = this.state.gameScore   1;
    if (challengeScore % 5 === 0) {
      this.setState({
        showChallengeQuestion: true,
        showQuizModal: true,
      });
      this.props.history.push({
        gameScore: this.state.gameScore,
      })
    };
  }

  render() {
    const addModalClose = () =>
      this.setState({
        showQuizModal: false,
      });
    return (
      <section>
        <WebHeader/>
        <div className="hackerGameContainer">
          <div className="gameStartButton">
            <Button variant="contained" color="primary"type="button" onClick={ this.startGame.bind(this) }> Start Game </Button>
          </div>
          <div className="hackerGame">
            <GameScore score={this.state.gameScore} display={this.state.display} />
            { this.createFirewalls() }
            <QuestionModal show={this.state.showQuizModal} onHide={addModalClose} context={this.state} score={this.state.gameScore} />
          </div>
        </div>
      </section>
    );
  }
}


export default Hacker;
  

Заранее спасибо!

Ответ №1:

this не привязан к gameOver обратному вызову, поэтому в конечном итоге он находится undefined в цепочке обещаний.

Либо привязать this к gameOver в конструкторе

 constructor(props) {
  super(props);
  this.state ={
    ...
  };
  this.handleChange = this.handleChange.bind(this);
  this.gameOver = this.gameOver.bind(this);
}
  

Или преобразовать gameOver в функцию со стрелкой, к которой будет автоматически привязан this вызывающий объект

 gameOver = () => {
  this.props.history.push({
    pathname: `/gameover`,
    gameScore: this.state.gameScore,
  });
};
  

checkAnswer является синхронным, поэтому кажется, что навигация по очереди происходит после обновления состояния очереди. this.props.onHide переключает addModalClose значение false в родительском компоненте и закрывает / отключает модальный. Попробуйте настроить подпись gameOver , чтобы использовать полезную нагрузку состояния маршрута, чтобы вложить копию состояния компонента из checkAnswer перед onHide помещением в очередь в стек вызовов.

 gameOver = (gameScore) => {
  this.props.history.push({
    pathname: `/gameover`,
    gameScore,
  });
};
  

Передайте значение состояния

 } else {
  console.log('incorrect');
  swal("So close!", this.state.explanation, "error",
    {
      closeOnClickOutside: false, 
      buttons: {
          confirm: "View your final score!"
      },
    }).then (function() {
      this.gameOver(this.state.gameScore); // <-- pass value
    });
}
this.props.onHide();
  

Примечание: Похоже, что swal управляет некоторым подтверждением, и это this.props.onHide должно запускаться после .then блоков любого swal вызова.

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

1. Привет! Спасибо, я только что попробовал это, но он все еще возвращается, так как не удается прочитать свойство ‘GameOver’ из undefined:(

2. @heygudetama Я не понимаю, почему this for this.gameOver было бы неопределенным ни с одним из упомянутых исправлений. Возможно, что где-то в вашем коде this не определено при попытке доступа this.state.gameOver . Есть ли у вас трассировка стека для ошибки, которой вы можете поделиться в своем вопросе, чтобы мы увидели, что было вызвано, номера строк и т.д. …?

3. Да, я тоже не уверен.. Я выполнил вход в консоль в checkAnswer, и он действительно показывает GameScore, если это то, что вы имеете в виду? Я также отредактировал вопрос, чтобы показать, где возникает ошибка. Спасибо за вашу помощь до сих пор 🙂

4. @heygudetama Так он захлебывается в этой строке this.gameOver(); ? Просто из любопытства, что this.props.onHide(); делает там в конце checkAnswer ?

5. Ага. Я также добавил основной код игры, теперь я думаю, может быть, в этом что-то есть:/ onHide() устанавливает состояние showQuizModal:false в Hacker.js чтобы скрыть модальность, когда был дан ответ на проблемный вопрос