Я создавал реагирующий компонент, который должен перерисовываться только после того, как была изменена одна из c опций redux. Это свойство redux является объектом и редуктором, который управляет этим объектом, возвращающим новое состояние с помощью Object.assign (). Проблема заключалась в том, что независимо от того, использует ли редуктор Object.assign () для создания нового состояния, компонент не реагирует на него, и его метод componentDidUpdate () не вызывается. Изменение Object.assign () на оператор распространения решило эту проблему, но я все еще не могу найти ответ на причину такого поведения, может кто-нибудь мне здесь поможет?
Вот как выглядит редуктор:
const INITIAL_GAME_CONFIG = Object.freeze({
mode: {
field: 5,
delay: 2000,
},
playerName: '',
});
function gameConfigReducer(state = { ...INITIAL_GAME_CONFIG }, { type, payload }) {
switch (type) {
case types.SET_GAME_CONFIG:
return Object.assign(state, payload);
default:
return state;
}
}
А это компонент:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Square from '../Square/';
import './Field.scss';
import gameStatuses from '../../../configs/gameStatuses';
import playerTypes from '../../../configs/playerTypes';
import { gameActions, gameOperations } from '../../../store/game';
const colorsConfig = Object.freeze({
GREY: 'grey',
GREEN: 'green',
RED: 'red',
BLUE: 'blue',
});
const fieldSquareStatuses = Object.freeze({
DEFAULT: 'default',
HIGHLIGHTED: 'highlighted',
TAKEN: 'taken',
LOST: 'lost',
});
const animationStatuses = Object.freeze({
scale: 'scale',
bigScale: 'bigScale',
});
const fieldSquareConfig = {
status: fieldSquareStatuses.DEFAULT,
animation: '',
};
class Field extends Component {
constructor(props) {
super(props);
this.state = {
fieldsMatrix: [],
};
}
// Simply creates the game field
createMatrix = (fieldSize) => {
const matrix = [];
let id = 0;
for (let i = 0; i < fieldSize; i++) {
let row = [];
for (let j = 0; j < fieldSize; j++) {
row.push({
id: id,
...fieldSquareConfig,
});
id = ++id;
}
matrix.push(row);
}
return matrix;
};
componentDidMount() {
console.log('mounted', this.state);
const matrix = this.createMatrix(this.props.gameConfig.mode.field);
this.setState({ fieldsMatrix: matrix });
}
componentDidUpdate(prevProps) {
console.log('updated', this.state);
if (prevProps.gameConfig.mode.field !== this.props.gameConfig.mode.field) {
console.log('hello');
const matrix = this.createMatrix(this.props.gameConfig.mode.field);
this.setState({ fieldsMatrix: matrix });
}
}
// This method searches a fieldMatrix for the neccessary element and returns it.
getSquareInMatrix = (id) => {
let square;
const matrix = [...this.state.fieldsMatrix];
matrix.forEach((el) => {
const searchResult = el.find((elem) => Number(elem.id) === Number(id));
if (searchResult) {
square = searchResult;
return;
}
});
return square;
};
// This method searches a fieldMatrix for the neccessary element, replaces it
// with the new one and updates the component state.
replaceSquareInMatrix = async (newElem) => {
const matrix = [...this.state.fieldsMatrix];
matrix.forEach((el) => {
const searchResult = el.find((elem) => Number(elem.id) === Number(newElem.id));
if (searchResult) {
el[el.indexOf(searchResult)] = newElem;
return;
}
});
await this.setState({
fieldsMatrix: matrix,
});
};
// This method replaces passed prop of a matrix element with the passed id.
replaceSquareProp = (id, prop, value) => {
const square = this.getSquareInMatrix(id);
const newSquare = {
...square,
[prop]: value,
};
this.replaceSquareInMatrix(newSquare);
};
onMouseOverSquare = (e) => {
this.replaceSquareProp(e.target.id, 'animation', animationStatuses.scale);
};
onAnimationEnd = (e) => {
this.replaceSquareProp(e.target.id, 'animation', '');
};
onSquareClick = (e) => {
this.replaceSquareProp(e.target.id, 'animation', animationStatuses.bigScale);
};
startGame = () => {};
render() {
return (
<div
className={`game-field ${
this.props.gameStatus && this.props.gameStatus === gameStatuses.STARTED
? ''
: 'disabled'
}`}
>
{this.state.fieldsMatrix.map((el) => {
return (
<div
className="game-field__row"
key={this.state.fieldsMatrix.indexOf(el)}
>
{el.map((el) => {
// Set square color status, depending on it's status
let color;
switch (el.status) {
case fieldSquareStatuses.DEFAULT:
color = colorsConfig.GREY;
break;
case fieldSquareStatuses.HIGHLIGHTED:
color = colorsConfig.BLUE;
break;
case fieldSquareStatuses.TAKEN:
color = colorsConfig.GREEN;
break;
case fieldSquareStatuses.LOST:
color = colorsConfig.RED;
break;
default:
color = colorsConfig.GREY;
break;
}
return (
<Square
id={el.id}
color={color}
key={el.id}
animation={el.animation}
onClick={this.onSquareClick}
onMouseOver={this.onMouseOverSquare}
onAnimationEnd={this.onAnimationEnd}
/>
);
})}
</div>
);
})}
</div>
);
}
}
const mapStateToProps = (state) => ({
gameConfig: state.game.gameConfig,
gameStatus: state.game.gameStatus,
loading: state.game.loading,
});
const MapDispatchToProps = {
setGameStatus: gameActions.setGameStatus,
updateWinners: gameOperations.updateWinners,
};
export default connect(mapStateToProps, MapDispatchToProps)(Field);
Вы можете найти весь проект здесь: https://github.com/JoyTailor-1775/clicker-game/tree/game-logic