React / Redux / Pure Component со свойством массива перерисовывается, когда массив не изменился - PullRequest
0 голосов
/ 08 января 2020

У меня есть приложение React / Redux, которое отображает сетку PureComponents. Я хочу, чтобы компоненты повторно отображались только при изменении значения реквизита, но на самом деле все компоненты повторно отображаются при любом обновлении для сохранения. Кажется, проблема вызвана свойством массива.

Я воспроизвел проблему в этой песочнице . В этом минимальном примере визуализируются два экземпляра компонента Cell. Каждый отображает значение, которое отдельно записывается в магазине, и может быть увеличено или уменьшено отдельно с помощью кнопки.

Ячейка. js

import React, { PureComponent } from "react";
import { connect } from "react-redux";

import { incrementAction, decreaseAction } from "./actions";

class Cell extends PureComponent {
  render() {
    const { index, value, incrementAction, decreaseAction } = this.props;
    console.log("render cell with index", index);
    return (
      <div>
        <h1>{value}</h1>
        <button onClick={incrementAction}>increment</button>
        <button onClick={decreaseAction}>decrease</button>
      </div>
    );
  }
}

const mapStateToProps = (state, ownProps) => ({
  value: ownProps.index === 1 ? state.value1 : state.value2,
  myArray: [0, 1, 2]
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  incrementAction: () => dispatch(incrementAction(ownProps.index)),
  decreaseAction: () => dispatch(decreaseAction(ownProps.index))
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Cell);

Если вы проверите консоль, вы должен увидеть, что при нажатии одной кнопки обе ячейки будут повторно отображаться.

Если вы закомментируете реквизит myArray в mapStateToProps, поведение изменится так, что только ячейка, по которой вы щелкнули, выполнит повторную визуализацию.

Похоже, что PureComponent выполняет повторный рендеринг при любом изменении хранилища из-за свойства массива stati c.

В моем реальном приложении массив также поступил бы из хранилища, и Cell должен повторно выполнить рендеринг, если значение массива изменяется, но в примере с песочницей показано, как даже свойство массива stati c вызывает повторный рендеринг.

Есть ли какой-нибудь способ предоставить массив свойств для PureComponent и получить его перерисовывать только при смене реквизита? Спасибо.

Редактировать: я обновил песочницу , чтобы переместить myArray в хранилище, как предложено Domino987, и добавить функцию для вычисления подмассива, требуемого компонентом Cell - это то, что делает мое настоящее приложение. Я добавил памятку с повторным выбором и повторным выбором и сделал ее функциональным компонентом вместо PureComponent. Насколько я вижу, теперь это работает - только одна ячейка перерисовывается при нажатии кнопки. Ууу!

В действиях. js:

import createCachedSelector from "re-reselect";

export function getMyArray(state, index) {
  console.log("getMyArrayCached for index", index);
  return state.myArray;
}

export function getIndex(state, index) {
  return index;
}

export const getMyArrayCached = createCachedSelector(
  getMyArray,
  getIndex,
  (myArray, index) =>
    myArray.map(elm => {
      return elm[index - 1];
    })
)((_state_, index) => index);

в редукторе. js:

const initialState = {
  value1: 0,
  value2: 0,
  myArray: [[1, 2], [1, 2], [1, 2]]
};

в ячейке. js:

const mapStateToProps = (state, ownProps) => {
  const value = ownProps.index === 1 ? state.value1 : state.value2;

  return {
  value,
  myArray: getMyArrayCached(state, ownProps.index)
}};

1 Ответ

2 голосов
/ 08 января 2020

Вы создаете новый массив при каждом вызове mapStateToProps с myArray: [0, 1, 2]. Из-за этого mapStateToProps всегда возвращает объект с новым экземпляром массива.

Redux, чем поверхностное сравнение предыдущих реквизитов. Ссылки myArray изменились, так как во время вызова oyu создал новый, и все ячейки обновляются. Удаляя строку myArray: [0, 1, 2], она работает как положено. Переместите этот массив в redux, чтобы не генерировать новый каждый вызов.

Чистый компонент также бесполезен, так как redux уже выполняет сравнение свойств, а чистый компонент снова делает то же самое, поэтому это тратится впустую, так как он всегда имеет один и тот же результат для избыточного и чистого компонента, и это просто больше работы.

Обновление: вы все еще создаете новый массив на каждом mapSateToPropsCall с функцией map в getMyArray, поскольку map возвращает новый массив. Почему вы не делаете массив stati c и не запоминаете его, чтобы генерировать новый, только если это необходимо?

Почему вы не измените способ сохранения данных, чтобы state.myArray[index] вернул необходимый массив?

И вы все еще можете преобразовать PureCompoennt в Component.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...