Как оптимизировать небольшие обновления реквизита вложенного компонента в React + Redux?

Пример кода:https://github.com/d6u/example-redux-update-nested-props/blob/master/one-connect/index.js

Посмотреть демо:http://d6u.github.io/example-redux-update-nested-props/one-connect.html

Как оптимизировать небольшие обновления реквизита вложенного компонента?

У меня есть вышеуказанные компоненты, Repo и RepoList. Я хочу обновить тег первого репо (Линия 14). Поэтому я отправилUPDATE_TAG действие. До того как я реализовалshouldComponentUpdateотправка занимает около 200 мсек, что ожидается, так как мы тратим много времени<Repo/>которые не изменились.

После добавленияshouldComponentUpdateДоставка занимает около 30 мс. После производственной сборки React.js обновления стоят всего около 17 мс. Это намного лучше, но временная шкала в консоли разработчика Chrome по-прежнему указывает на джанк-фрейм (более 16.6 мс)

Представьте, если у нас будет много таких обновлений или<Repo/> более сложный, чем текущий, мы не сможем поддерживать 60fps.

Мой вопрос заключается в том, что для таких небольших обновлений реквизита вложенного компонента существует ли более эффективный и канонический способ обновления содержимого? Могу ли я использовать Redux?

Я получил решение, заменив каждыйtags с наблюдаемым внутри редуктором. Что-то вроде

// inside reducer when handling UPDATE_TAG action
// repos[0].tags of state is already replaced with a Rx.BehaviorSubject
get('repos[0].tags', state).onNext([{
  id: 213,
  text: 'Node.js'
}]);

Затем я подписываюсь на их значения внутри компонента Repo, используяhttps://github.com/jayphelps/react-observable-subscribe, Это сработало отлично. Каждая отправка стоит всего 5 мс даже при разработке сборки React.js. Но я чувствую, что это анти-паттерн в Redux.

Обновление 1

Я последовал рекомендации в ответе Дана Абрамова инормализовал мое состояние а такжеобновлены компоненты подключения

Новая государственная форма:

{
    repoIds: ['1', '2', '3', ...],
    reposById: {
        '1': {...},
        '2': {...}
    }
}

я добавилconsole.time вокругReactDOM.render ко времениначальный рендеринг.

Тем не менее, производительность хуже, чем раньше (как начальный рендеринг, так и обновление). (Источник:https://github.com/d6u/example-redux-update-nested-props/blob/master/repo-connect/index.js, Живая демонстрация:http://d6u.github.io/example-redux-update-nested-props/repo-connect.html)

// With dev build
INITIAL: 520.208ms
DISPATCH: 40.782ms

// With prod build
INITIAL: 138.872ms
DISPATCH: 23.054ms

Я думаю подключиться на каждом<Repo/> имеет много накладных расходов.

Обновление 2

Основываясь на обновленном ответе Дэна, мы должны вернутьсяconnect«smapStateToProps аргументы возвращают функцию вместо этого. Вы можете проверить ответ Дэна. Я тоже обновилдемки.

Ниже производительность намного лучше на моем компьютере. И просто для удовольствия, я также добавил побочный эффект в подходе редуктора, о котором я говорил (источник, демонстрация) (серьезно не используйте его, это только для эксперимента).

// in prod build (not average, very small sample)

// one connect at root
INITIAL: 83.789ms
DISPATCH: 17.332ms

// connect at every <Repo/>
INITIAL: 126.557ms
DISPATCH: 22.573ms

// connect at every <Repo/> with memorization
INITIAL: 125.115ms
DISPATCH: 9.784ms

// observables + side effect in reducers (don't use!)
INITIAL: 163.923ms
DISPATCH: 4.383ms
Обновление 3

Только что добавленреактивно-виртуализированный пример основанный на «подключиться к каждому с запоминанием»

INITIAL: 31.878ms
DISPATCH: 4.549ms

Ответы на вопрос(1)

Ваш ответ на вопрос