Пытаюсь изменить ход игры при нажатии на кнопку, но это не работает

#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 , он отображает первый вместо первых двух. И так продолжается и дальше.