状态历史在反应中没有正确更新
state history not updating properly in react
我正在建造一座河内塔游戏来习惯反应。我有一个名为 "disks" 的状态 属性,它是由 3 个长度为 N 的数组组成的数组(N 是磁盘的总数)。我还定义了一个状态 属性 "history" 应该包含这样的磁盘阵列的历史:
- 最初:历史=[磁盘(初始配置)]
- 1 次移动后: 历史记录 = [磁盘(初始配置),磁盘(1 次移动后)]
- 2 次移动后: history = [disks(Initial config), disks(after 1 move), disks(after 2 move)] etc.
然而,M移动后,历史数组如下所示:
历史 = [磁盘(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);
}
请注意,游戏在其他方面正常运行,这意味着磁盘阵列正在正确更新等...只是历史阵列的更新出了点问题。我尝试将 disks.slice() 放入 history.concat 中,因为在我看来,历史以某种方式存储了对磁盘阵列的引用,但这没有帮助。
问题来源于此:
disks[i].push(disks[selected].pop());
这会改变索引 i
处的 disk
并改变选定的 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
的注意事项:如您所见,它仅对 a 进行浅表复制
一个数组,这意味着如果你有一个嵌套对象并且只做外部对象的浅拷贝
然后在里面 mutate ,原来的副本也会发生变异。您可能想创建一个 deepCopy
。
我正在建造一座河内塔游戏来习惯反应。我有一个名为 "disks" 的状态 属性,它是由 3 个长度为 N 的数组组成的数组(N 是磁盘的总数)。我还定义了一个状态 属性 "history" 应该包含这样的磁盘阵列的历史:
- 最初:历史=[磁盘(初始配置)]
- 1 次移动后: 历史记录 = [磁盘(初始配置),磁盘(1 次移动后)]
- 2 次移动后: history = [disks(Initial config), disks(after 1 move), disks(after 2 move)] etc.
然而,M移动后,历史数组如下所示:
历史 = [磁盘(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);
}
请注意,游戏在其他方面正常运行,这意味着磁盘阵列正在正确更新等...只是历史阵列的更新出了点问题。我尝试将 disks.slice() 放入 history.concat 中,因为在我看来,历史以某种方式存储了对磁盘阵列的引用,但这没有帮助。
问题来源于此:
disks[i].push(disks[selected].pop());
这会改变索引 i
处的 disk
并改变选定的 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
的注意事项:如您所见,它仅对 a 进行浅表复制
一个数组,这意味着如果你有一个嵌套对象并且只做外部对象的浅拷贝
然后在里面 mutate ,原来的副本也会发生变异。您可能想创建一个 deepCopy
。