#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
forthis.gameOver
было бы неопределенным ни с одним из упомянутых исправлений. Возможно, что где-то в вашем кодеthis
не определено при попытке доступаthis.state.gameOver
. Есть ли у вас трассировка стека для ошибки, которой вы можете поделиться в своем вопросе, чтобы мы увидели, что было вызвано, номера строк и т.д. …?3. Да, я тоже не уверен.. Я выполнил вход в консоль в checkAnswer, и он действительно показывает GameScore, если это то, что вы имеете в виду? Я также отредактировал вопрос, чтобы показать, где возникает ошибка. Спасибо за вашу помощь до сих пор 🙂
4. @heygudetama Так он захлебывается в этой строке
this.gameOver();
? Просто из любопытства, чтоthis.props.onHide();
делает там в концеcheckAnswer
?5. Ага. Я также добавил основной код игры, теперь я думаю, может быть, в этом что-то есть:/
onHide()
устанавливает состояниеshowQuizModal:false
в Hacker.js чтобы скрыть модальность, когда был дан ответ на проблемный вопрос