история государства не обновляется должным образом в реакции - PullRequest
0 голосов
/ 04 сентября 2018

Я строю игру Ханойская башня, чтобы привыкнуть реагировать. У меня есть свойство состояния, называемое «дисками», которое представляет собой массив, состоящий из 3 массивов длины N (N - общее количество дисков). Я также определил свойство состояния "history", которое должно содержать историю массива дисков, например:

  1. изначально: history = [диски (начальная конфигурация)]
  2. После 1 хода: history = [диски (начальная конфигурация), диски (после 1 хода)]
  3. После 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);
  }

Обратите внимание, что игра работает по-другому, то есть массив дисков обновляется корректно и т. Д. Это просто обновление массива истории, которое почему-то идет не так. Я попытался вставить в файл history.concat disk.slice (), так как мне показалось, что история каким-то образом хранит ссылки на массив дисков, но это не помогло.

1 Ответ

0 голосов
/ 04 сентября 2018

Проблема заключается в следующем:

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.

...