У меня есть приложение React / Redux, в котором я рисую двумерную сетку. Данные сетки находятся в хранилище Redux в виде массива массивов. например, для сетки 3x2 это может выглядеть следующим образом:
state.grid = [
[{value: 1},{value: 2}],
[{value: 0},{value: 1}],
[{value: 1},{value: 3}]
]
Пользователь может щелкнуть квадрат сетки, чтобы изменить его значение. Поскольку сетка может быть большой, я хочу обновить только ячейку, которая была изменена. Но что бы я ни пытался, вся сетка обновляется.
Я использую повторный выбор с повторный выбор для запоминания отдельных селекторов, но я не думаю, что работает. Вот упрощенная версия моего кода:
GridCell.js:
class GridCell extends PureComponent {
render() {
const { valueObj } = this.props;
return <span>valueObj.value</span>
}
function mapStateToProps(state, ownProps) {
const { rowIndex, columnIndex } = ownProps;
const valueObj = getValueCached(state, `${columnIndex}_${rowIndex}`);
console.log('which cell', `${columnIndex}_${rowIndex}`);
return {valueObj};
}
export default connect(mapStateToProps)(GridCell);
Также есть родительский компонент, который dr aws сетка GridCells, передавая каждому из своих columnIndex и rowIndex в качестве реквизитов:
class MyGrid extends PureComponent {
const columns = 3;
const rows = 2;
render() {
columns.map((column) => {
rows.map((row) => {
<GridCell
columnIndex={column}
rowIndex={row}
/>
}
}
и в моем магазине Redux:
import createCachedSelector from 're-reselect';
export const getValueFromCache = (state, identifier) => {
const identifiers = identifier.split('_');
return state.grid[identifiers[0]][identifiers[1]];
};
export const getValueCached = createCachedSelector(
getValueFromCache,
// resultFunc
(value) => value,
)(
// re-reselect keySelector (receives selectors' arguments)
// Use "columnIndex_rowIndex" as cacheKey
// re-reselect only accepts string as cacheKey
(_state_, identifier) => identifier
);
Когда пользователь щелкает квадрат сетки, журнал «какая ячейка» в функции mapStateToProps записывается в консоль для каждой ячейки сетки.
Редактировать: я использую updeep внутри моего редуктора для обновления значений, что-то вроде этого:
case UPDATE_GRID_CELL: {
{ newValue } = action.payload;
return updeep({
'grid': { [columnIndex]: { [rowIndex]: newValue } },
state);
}
Я также пытался получить значение в виде простой проп с подключением :
function mapStateToProps(state, ownProps) {
const { rowIndex, columnIndex } = ownProps;
return {state.grid[columnIndex][rowIndex].value};
}
и я установил проверку, чтобы увидеть, изменились ли какие-либо реквизиты или состояние:
componentDidUpdate(prevProps, prevState) {
console.log('componentWillUpdate');
console.log('new', JSON.stringify(this.props));
console.log('old', JSON.stringify(prevProps));
console.log('changed props', JSON.stringify(this.props) !== JSON.stringify(prevProps));
console.log('state');
console.log('new', JSON.stringify(this.state));
console.log('old', JSON.stringify(prevState));
console.log('changed state', JSON.stringify(this.state) !== JSON.stringify(prevState));
}
Это регистрирует ложь, но метод render () запускается, как показывается консольным журналом. Я озадачен, потому что я думал, что смысл pureComponent в том, что он рендерится только в том случае, если его состояние или реквизиты изменились?
Я ожидал, что памятка предоставит те же значения для незатронутых ячеек, и для этого не будет срабатывать обновление компонента. Но я должен что-то упустить.
Я не знаю, как проверить, работает ли памятка. Если я помещаю console.log в getValueCached (), он пишет для каждой ячейки, но я не знаю, будет ли это так или иначе ...?
Есть ли какой-нибудь способ предотвратить каждый компонент ячейки сетки от обновление при изменении одного значения? Спасибо за любые предложения.
Редактировать: из комментариев / ответа и дальнейшего изучения, кажется, что повторные рендеры вызваны массивом prop. Я создал новый вопрос , который иллюстрирует это на примере минимальной песочницы.