#javascript #reactjs #setstate
Вопрос:
Я создаю настольную игру в react.js и теперь я пытаюсь добавить функцию, которая позволяет пользователю просматривать игру. Я думал, что справился, но это не работает. Я проверил, являются ли мои объекты неизменяемыми(они должны быть), я также проверил, вызывается ли функция, которая должна возвращать игровой шаг, и никаких проблем тоже нет. Я не знаю, почему это не работает. Как я могу это решить?
Вот функция конструктора с игровыми данными:
constructor(props) {
super(props);
this.state = {
// The Game Data Comes Here
history: [{
squares: [
Array(8).fill(null),
Array(8).fill(null),
Array(8).fill(null),
[null,null,null,'black','white',null,null,null],
[null,null,null,'white','black',null,null,null],
Array(8).fill(null),
Array(8).fill(null),
Array(8).fill(null)
]
}],
stepNumber: 0,
blackIsNext: true,
winner: null
}
}
Вот где я оказываю:
render() {
// datas that will be rendered regularly come here
const history = this.state.history
const current = history[this.state.stepNumber]
const moves = history.map((_, move) => {
const desc = move ? 'Go to move #' move : 'Go to game start';
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
// Return the game scene here
return (
<div className="master-container">
<GameBoard squares={current.squares} onClick={(row,col) => {
if (!this.state.winner) {
const elems = this.checkElementsAround(this.checkEmptySpaces())
for (let el=0;el<elems.length;el ) {
const turning = this.checkTurningStones(elems[el].directions, this.state.blackIsNext)
if (turning.length !== 0) {
turning.unshift([row,col])
if (row === elems[el].coordinates[0] amp;amp; col === elems[el].coordinates[1]) {
this.handleMove(turning)
this.setWinnerAndTurn()
// Debug
//console.log(history.length)
console.log(moves)
break
}
}
}
}
}}/>
<div>{(!this.state.winner) ? "Player Turn: " `${(this.state.blackIsNext) ? 'Black' : 'White'}` : 'WINNER: ' this.state.winner}</div>
<div>{(this.state.winner) ? moves : null}</div>
</div>
)
}
Есть некоторые функции, которые я не буду помещать, потому что то, что они делают, почти не имеет значения, они не изменяют данные. А также я не буду использовать функцию setWinnerAndTurn, так как она определяет победителя игры только в том случае, если игра закончена или она переключает ход игрока, но проблема здесь, должно быть, в том, как я обрабатываю history
данные.
Функция, которая обрабатывает ходы, и та, которая переходит на другой шаг игры
handleMove(cords) {
// You'll return if the game is already over or the value of the square is NOT null
if (this.state.winner) {
return
}
// Handle the recently made move here
const history = this.state.history.slice(0, this.state.stepNumber 1);
const current = history[this.state.stepNumber];
const squares = current.squares.slice()
// You'll handle the click here
for (var i=0;i<cords.length;i ) {
squares[cords[i][0]][cords[i][1]] = (this.state.blackIsNext) ? "black" : "white"
}
this.setState({
history: history.concat([{squares: squares}]),
stepNumber: history.length,
});
}
jumpTo(step) {
this.setState({
stepNumber: step,
blackIsNext: (step % 2) === 0,
});
}
Если вы считаете, что для решения проблемы чего-то не хватает, пожалуйста, дайте мне знать.
Комментарии:
1. На первый взгляд я думаю, что ваша проблема в том , что вы не переключаетесь, что является операцией мутации. Правка: а также мутация здесь:
squares[cords[i][0]][cords[i][1]] =
2. Я думаю, что нашел проблему. Отладив, я понял, что все элементы внутри массива истории обновляются
3. Да, но это не будет храниться, просто временно там.
4. Ну, элементы квадратов могут быть изменены, так как они будут переданы в качестве нового значения квадратов
5. Это мутация, потому
squares
что это неглубокая копия массива из состояния.squares
Массив новый, но массивы внутриsquares
него те же самые. Вы имеете дело с глубоко вложенными данными, поэтому их действительно трудно обновить без мутаций . Я бы честно рекомендовал такого помощника, как иммер .
Ответ №1:
Операция назначения в этой строке является мутацией состояния вашего компонента.
squares[cords[i][0]][cords[i][1]] = (this.state.blackIsNext) ? "black" : "white"
Это мутация, потому squares
что это неглубокая копия массива из this.state
. squares
Массив новый, но массивы внутри квадратов те же самые.
Вы имеете дело с глубоко вложенными данными, поэтому их действительно трудно обновить без мутаций. Я бы честно рекомендовал такого помощника, как immer, который позволяет выполнять операции назначения в состоянии черновика.
Без помощника вам придется сопоставлять squares
и изменять внутренние массивы (по крайней мере, те, в которых изменены элементы). Я думаю, что это правильно, но дважды проверьте, что у меня нет перепутанных строк и столбцов.
// Handle the recently made move here
const history = this.state.history.slice(0, this.state.stepNumber 1);
const current = history[this.state.stepNumber];
// Apply changes to every square
const color = this.state.blackIsNext ? "black" : "white";
const nextSquares = current.squares.map((row, y) =>
row.map((square, x) =>
// find if this [x,y] is in the cords array and replace it if it is
cords.some((cord) => cord[0] === x amp;amp; cord[1] === y) ? color : square
)
);
// Update the state
this.setState({
history: history.concat({ squares: nextSquares }),
stepNumber: history.length
});
Комментарии:
1. Это помогло, но сейчас у меня все еще есть проблема. массив истории приходит сзади. После того , как я помещаю элемент в массив истории с помощью
concat
, он отображает первый вместо первых двух. И так продолжается и дальше.