React / Redux: как обновить одну ячейку сетки без повторного рендеринга всех остальных - PullRequest
0 голосов
/ 07 января 2020

У меня есть приложение 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. Я создал новый вопрос , который иллюстрирует это на примере минимальной песочницы.

1 Ответ

0 голосов
/ 07 января 2020

Я думаю, вы неправильно поняли функцию mapStateToProps. Он будет вызываться для каждого обновления состояния избыточности и должен выяснить, изменились ли значения для этого. Поэтому он должен вызывать каждый mapStateToProps для каждой ячейки.

Это не значит, что он переопределяет этот компонент. Если предыдущие и текущие возвращаемые значения mapStateToProps совпадают, обновление будет прервано.

Так что правильно вызывать mapStateToProps. Проверьте, действительно ли ячейки перерисованы. Вы также можете удалить оболочку getValueCached и получить прямой доступ к сетке с помощью state.grid[identifiers[0]][identifiers[1]]. Это будет быстрее, чем кэшированная версия, так как это просто прямой доступ. И вы не получите никакой выгоды от кэшированной версии, поскольку она не требует больших вычислений.

Возможно, будет даже быстрее подключить самый высокий компонент и jsut разрешить его повторное рендеринг при необходимости. Другой вариант - использовать реагирующее окно только для рендеринга текущих видимых элементов сетки, что значительно улучшит вашу производительность.

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