@Incold

Почему неправильно меняется значение двумерного массива в action?

Недавно начал изучать React, тренируюсь по туториалу на сайте React (делаю крестики-нолики, но с использованием Redux + параметры игры задаёт юзер), и столкнулся с проблемой:
Значение в двумерном массиве (значения ячеек поля) меняется очень странно, должно меняться только в одной ячейке, а меняется значение всего столбца (именно столбца).

Компонент поля:

class Board extends React.Component {
  componentDidMount() {
    this.props.setSquareValue() // нужно чтобы заполнить все ячейки null значениями
  }

  render() {
    return (
      <div id="board">
        {this.props.squaresValue.map((item , i) =>
          <div className="row" key={`row_${i}`}>
            {item.map((square, j) =>
              <div key={`row_${i}-col_${j}`} className="col p-0">
                <Square location={[i, j]} squareValue={square}/>
              </div>
            )}
          </div>
        )}
      </div>
    );
  }
}

export default connect(
  state => ({
    squaresValue: state.game.squaresValue
  }),
  dispatch => ({
    setSquareValue(num) {
      dispatch(setSquareValue(num));
    }
  })
)(Board);

Компонент ячейки:

function Square (props) {
  return (
    <div className="cell d-flex align-items-center justify-content-center">
      <button className="btn" onClick={()=>{
        props.setSquareValue(props.location, 'X');
      }}>
        {props.squareValue} 
      </button>
    </div>
  );
}

export default connect(
  null,
  dispatch => ({
    setSquareValue(num, value) {
      dispatch(setSquareValue(num, value));
    }
  })
)(Square);

Action:

export const setSquareValue = (num, value) => {
  return (dispatch, getState) => {
    if (num === undefined && value === undefined) { 
      let width = getState().gameParameters.fieldWidth;
      let height = getState().gameParameters.fieldHeight;
      return dispatch({ type:'SET_SQUARE_VALUE', values: Array(height).fill(Array(width).fill(null)) });
    }
    else {  // Здесь происходит самое странное
      let array = getState().game.squaresValue; // получаю двумерный массив значений
      console.log(array);  // Здесь почему-то массив уже изменён, хотя значение меняю в следующей строке
Т.е. если поле 3х3, и я нажимаю на ячейку [0,0], то значения поменяют ещё и ячейки [1,0] и [2,0]
      array[num[0]][num[1]] = value; // num (координаты ячейки) приходит нормальный
      return dispatch({ type:'SET_SQUARE_VALUE', values: array });
    }
  }
}
  • Вопрос задан
  • 239 просмотров
Решения вопроса 1
0xD34F
@0xD34F Куратор тега React
values: Array(height).fill(Array(width).fill(null))

В качестве элементов массива values используется один и тот же массив. Меняйте на

values: Array.from({ length: height }, () => Array(width).fill(null))

UPD. Ну а в целом - дичь какая-то у вас. Собирать новое состояние игрового поля надо в редюсере. Экшенов должно быть два - отдельно для сброса поля, и для совершения хода.

Примерно так:
const initialState = {
  player: 'X',
  cells: Array.from({ length: 3 }, () => Array(3).fill(null)),
};

const store = createStore((state = initialState, action) => {
  switch (action.type) {
    case 'MOVE':
      return {
        ...state,
        player: state.player === 'X' ? 'O' : 'X',
        cells: state.cells.map((row, iRow) => iRow === action.location[0]
          ? row.map((cell, iCol) => iCol === action.location[1] ? state.player : cell)
          : row
        ),
      };

    case 'RESET':
      return initialState;

    default:
      return state;
  }
});

const Board = connect(({ cells }) => ({ cells }))(({ cells }) => (
  <div class="board">
    {cells.map((row, iRow) =>
      <div className="row">
        {row.map((cell, iCol) =>
          <Cell location={[ iRow, iCol ]} value={cell} />
        )}
      </div>
    )}
  </div>
));

const Cell = connect(null, (dispatch, { location }) => ({
  onClick: () => dispatch({ type: 'MOVE', location }),
}))(({ value, onClick }) => (
  <div className="cell">
    <button onClick={onClick} disabled={value !== null}>
      {value}
    </button>
  </div>
));

const Reset = connect(null, dispatch => ({
  reset: () => dispatch({ type: 'RESET' }),
}))(({ reset }) => (
  <button onClick={reset}>RESET</button>
));

const App = () => (
  <Provider store={store}>
    <Reset />
    <Board />
  </Provider>
);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы