1

我正在建造河内塔游戏以适应反应。我有一个名为“disks”的状态属性,它是一个由 3 个长度为 N 的数组组成的数组(N 是磁盘的总数)。我还定义了一个状态属性“历史”,它应该包含磁盘阵列的历史,如下所示:

  1. 最初:历史= [磁盘(初始配置)]
  2. 1 次移动后: history = [磁盘(初始配置),磁盘(1 次移动后)]
  3. 2 次移动后: history = [disks(Initial config), disks(1 次移动后), disks(2 次移动后)] 等。

然而,在 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 中,因为在我看来历史以某种方式存储了对磁盘数组的引用,但这并没有帮助。

4

1 回答 1

0

问题来自于此:

disks[i].push(disks[selected].pop());

这会改变diskat 索引i并改变 selected 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
  };
}

你看我为不同的部分介绍了几个函数isLegalMoveupdateDisk这些是为了分离关注点并让测试更容易。

关于使用的注意事项Array.prototype.slice: 正如您所注意到的,它只对数组进行浅拷贝,这意味着如果您有一个嵌套对象并且只对外部对象进行浅拷贝,然后在其中发生变异,则原始副本将也会变异。您可能想要创建一个deepCopy

于 2018-09-04T12:05:47.680 回答