История государства не обновляется должным образом в реакции
Я строю игру Ханойская башня, чтобы привыкнуть реагировать. У меня есть свойство состояния, называемое "дисками", которое представляет собой массив, состоящий из 3 массивов длины N (N - общее количество дисков). Я также определил свойство состояния "history", которое должно содержать историю массива дисков следующим образом:
- изначально: history = [диски (начальная конфигурация)]
- После 1 хода: history = [диски (начальная конфигурация), диски (после 1 хода)]
- Через 2 хода: history = [диски (начальная конфигурация), диски (после 1 хода), диски (после 2 хода)] и т. Д.
Тем не менее, после перемещения M, массив истории выглядит так:
history = [диски (после M перемещений), диски (после M перемещений),..., диски (после M перемещений)].
Я не могу найти свою ошибку. Был бы признателен, если бы кто-нибудь имел представление о том, что происходит не так. Вот соответствующий код:
constructor(props){
super(props);
let disks = [
[],
[],
[]
];
//Initially all disks in first tower
for(let i=0; i<props.numberOfDisks; i++){
disks[0].push(i);
}
this.state = {
disks : disks,
selected : null,
move: 0,
history: [disks]
};
}
handleClick(i){
const disks = this.state.disks.slice();
const history = this.state.history.slice();
let move = this.state.move;
let selected = this.state.selected;
//if user has not previously selected a tower or selects the same tower again
if(selected===null || i===selected){
selected = disks[i].length>0 && i!==selected ? i : null;
this.setState({
selected : selected
});
return;
}
//Check if move is legal
//index is at bottom is 0 and the largest disk has id 0
if(disks[i].length === 0 || disks[i][disks[i].length-1] < disks[selected][disks[selected].length-1]){
//perform move
disks[i].push(disks[selected].pop());
move++;
// I guess this is where it goes wrong, but I can't see why
this.setState({
history: history.concat([disks]),
disks: disks,
move: move
});
}
this.setState({
selected: null
});
console.log(this.state.history);
}
Обратите внимание, что игра работает иначе, то есть массив дисков обновляется корректно и т. Д. Это просто обновление массива истории, которое как-то идет не так. Я попытался поместить диски.s.sice () в history.concat, так как мне показалось, что история каким-то образом хранит ссылки на массив дисков, но это не помогло.
1 ответ
Проблема исходит из этого:
disks[i].push(disks[selected].pop());
Это мутирует disk
по указателю i
на месте и мутирует выбранный disk
, Потому что вы храните ссылки на тезисы в history
и продолжайте добавлять ссылки на эти объекты в history
то, что вы наблюдаете, - ваша игра стабилизируется.
Чтобы немного лучше понять, что происходит, вы можете попытаться разделить handleClick
метод на несколько частей.
function getNewState (oldState, selectedIndex) {
if (oldState.selected === selectedIndex) {
return oldState;
}
if (isLegalMove(oldState, selectedIndex)) {
return {
...oldState,
selected: selectedIndex, //updated the index
disks: updateDisk(oldState.disks, selectedIndex),
move: oldState.move + 1
};
}
return {
...oldState,
selected: null
};
}
Вы видите, что я ввел несколько функций для разных частей isLegalMove
а также updateDisk
они должны разделить проблемы и сделать тестирование более простым.
Примечание относительно использования Array.prototype.slice
: как вы заметили, это делает только поверхностную копию массива, это означает, что если у вас есть вложенный объект, и вы делаете только поверхностную копию внешнего, а затем изменяете его, оригинальная копия также будет видоизменяться. Вы можете создать deepCopy
вместо.