Я создаю свое первое приложение реагирования и столкнулся с такой ситуацией, когда мне приходится дважды вызывать this.setState () в методе. один из этих вызовов также выполняется внутри вложенной функции, вызываемой из метода.
В моем случае я строю игру тральщика, и я пытаюсь добиться того, чтобы выиграть, действительно ли игрок выиграл, правильно отметив все мины. Игрок выигрывает, когда устанавливает флаги над каждой ячейкой, в которой находится мина, поэтому вы не можете нажать на нее.
Для этого я создал метод checkIfWin (), который выполняет следующие действия:
- хранить в массиве информацию о плате (каждая ячейка со своими свойствами) и в счетчике количество оставшихся мин. Оба извлекаются из состояния компонента в две переменные, называемые data и minesLeft
- Если осталось 0 мин (пользователь отметил столько ячеек, сколько мин в игре), то вы сравниваете два массива ( minesArray , содержащий каждую позицию мин на доске и flagsArray содержит каждую позицию флага)
- Если массивы содержат одинаковую информацию (флаги покрывают каждую шахту), вы выигрываете. Если массивы отличаются, продолжайте играть.
Эта функция вызывается из двух разных мест
- После того, как игрок щелкнул последнюю ячейку, в которой нет мины, и каждая клетка, содержащая флаг, была правильно помечена
- После того, как игрок пометил ячейку и оставшиеся мины на доске равны 0
В первой ситуации проблем нет, все отлично работает. Я проверяю, является ли minesLeft в статусе === 0, и если это так, я вызываю метод checkIfWin () . Поскольку minesLeft уменьшается только после пометки ячейки, а не щелчка по ней, здесь нет проблем.
Но после того, как игрок пометил ячейку, я не могу найти решение, чтобы объявить, выиграл игрок или нет. Это логика моего флага ():
- Проверьте, накрыт ли тайл (на него не нажимали) и не выиграл ли игрок еще (чтобы не нажать на тайл после окончания игры)
- Если плитка закрыта и помечена, разблокируйте ее и обновите счетчик.
- Если плитка покрыта, и она не помечена, закройте ее флагом и уменьшите счетчик minesLeft .
- Когда minesLeft равен 1, а игрок помечает новую ячейку, minesLleft теперь равен 0, поэтому я должен проверить выигрыш.
- Теперь это , где я хотел бы проверить, выиграл ли игрок . Для этого мне нужно было бы вызвать this.setState (), чтобы обновить flagsArray с позицией нового флага на плате и последующего вызова checkIfWin (), который извлекает значение, хранящееся внутри состояния, также из этого метода.
Это приводит к вызову this.setState () для пакетной обработки перед выполнением полностью , поэтому при попытке сравнить, если minesLeft === 0 внутри checkIfWin (), значение хранится в состоянии - это значение состояния, которое было до последнего пометки (1 вместо 0), оно не обновлялось из-за логики стека Reac't для вызовов this.setState (). Также упоминается в статье JusticeMba на среднем
React может объединять несколько вызовов setState () в одно обновление для повышения производительности.
Итак ЧТО Я ХОЧУ ЗНАТЬ (извините за длину вопроса), есть ли способ, которым я мог бы сделать это? Я фактически связываю этот метод пометки как обработчик щелчка правой кнопкой мыши на компоненте внутри render () платы. Код будет следовать ниже. Меня не волнует, знаете ли вы какое-нибудь решение в коде или псевдокоде. Я бы взял и то и другое, так как мне кажется, что я не могу найти правильного решения для этого.
Код:
Inside Board.js render ()
render() {
const board = this.renderBoard(this.state.boardData);
return (
<div className={classes.board}>
<div className={classes.gameInfo}>
<h1>
{this.state.gameStatus}
</h1>
<span className={classes.info}>
Mines remaining: {this.state.mineCount}
</span>
</div>
{board} //This is a Board component that holds Tiles to which the rightClickHandler() is binded as a prop
</div>
RightClickHandler (), который управляет пометкой плиток:
rightClickHandler(event, x, y) {
event.preventDefault(); // prevents default behaviour such as right click
const clickedTile = { ...this.state.boardData[x][y] }
//ommit if revealed and if game is ended
if (!clickedTile.isRevealed && !(this.state.gameStatus === "YOU WON!")) {
let minesLeft = this.state.mineCount;
//if not revealed it can be flagged or not
if (clickedTile.isFlagged) {
//if flagged, unflag it
clickedTile.isFlagged = false;
minesLeft++;
} else {
//if not flagged, flag it
clickedTile.isFlagged = true;
minesLeft--;
}
//Update the state with new tile and check game status
const updatedData = [...this.state.boardData];
updatedData[x][y] = { ...clickedTile };
this.setState({
boardData: updatedData,
mineCount: minesLeft,
});
//THIS IS WHERE I WOULD LIKE TO checkIfWin()
}
Наконец, checkIfWin ():
//Check game progress and returns a boolean: true if win, false if not.
checkIfWin() {
//No need to create a new array, Ill just reference the one inside state
const data = this.state.boardData;
const minesLeft = this.state.mineCount;
//If flagged as many tiles as mines were initially
if (minesLeft === 0) {
//get mines as an array of positions
const mineArray = this.getMines(data);
//get flags as array of positions
const flagArray = this.getFlags(data);
if (JSON.stringify(mineArray) === JSON.stringify(flagArray)) {
//if both arrays are equal, game is won
return true;
}
} else {
//if more than 0 mines are left return false
return false;
}
}
TL; DR: я пытаюсь получить доступ к значению свойства компонента с устаревшим состоянием, которое, как предполагалось, было обновлено из предыдущего вызова this.setState () внутри обработчика щелчка компонента JSX внутри render ()
Извините за длину, и я надеюсь, что это понятно, если нет, я могу отредактировать для ясности.