Ответ 1
Это не проблема, присущая Redux IMHO.
Кстати, вместо того, чтобы пытаться отображать 100k компонентов одновременно, вы должны попытаться подделать его с помощью lib, например react-infinite или что-то подобное, и отображать только видимые (или близкие к нему) элементы вашего списка. Даже если вам удастся отобразить и обновить список 100 тыс., Он все еще не работает и требует большой памяти. Вот несколько Рекомендации LinkedIn
Этот anwser рассмотрит, что вы по-прежнему пытаетесь отображать обновляемые элементы 100k в своей DOM и что вы не хотите, чтобы слушатели 100k (store.subscribe()
) вызывались при каждом изменении.
2 школы
При разработке приложения пользовательского интерфейса функциональным способом вы в основном имеете 2 варианта:
Всегда выводить с самого верхнего уровня
Он работает хорошо, но включает в себя больше шаблонов. Это не совсем предложенный метод Redux, но достижимый, с некоторыми недостатками. Обратите внимание, что даже если вам удастся подключиться к одному редукционному соединению, вам все равно придется называть много shouldComponentUpdate
во многих местах. Если у вас есть бесконечный стек представлений (например, рекурсия), вам придется отображать в качестве виртуального dom все промежуточные представления, а shouldComponentUpdate
будет вызываться на многих из них. Таким образом, это не очень эффективно, даже если у вас есть одно подключение.
Если вы не планируете использовать методы жизненного цикла React, а используете только чистые функции рендеринга, тогда вам, вероятно, следует рассмотреть другие аналогичные параметры, которые будут сосредоточены только на этой задаче, например deku (который можно использовать с Redux)
В моем собственном опыте это с React недостаточно эффективно для старых мобильных устройств (например, Nexus4), особенно если вы связываете текстовые входы с вашим состоянием атома.
Подключение данных к дочерним компонентам
Это то, что предлагает react-redux, используя connect
. Поэтому, когда государство изменяется, и оно связано только с более глубоким ребенком, вы только визуализируете этого ребенка и не должны отображать компоненты верхнего уровня каждый раз, как поставщики контекста (сокращение/intl/custom...), а также основной макет приложения. Вы также не вызываете shouldComponentUpdate
для других дочерних элементов, потому что он уже запекается в слушателе. Вызов множества очень быстрых слушателей, вероятно, быстрее, чем рендеринг, каждый раз, когда промежуточные компоненты реагируют, а также позволяет уменьшить количество шаблонов, проходящих через реквизит, поэтому для меня это имеет смысл при использовании с React.
Также обратите внимание, что сравнение идентичности происходит очень быстро, и вы можете легко их выполнять при каждом изменении. Помните Angular грязную проверку: некоторым людям удалось создать реальные приложения с этим! И сравнение идентичности происходит намного быстрее.
Понимание вашей проблемы
Я не уверен, что понимаю всю вашу проблему, но я понимаю, что у вас есть представления с похожими элементами в 100 000, и вам интересно, следует ли использовать connect
со всеми этими 100k элементами, потому что вызывать 100k слушателей при каждом изменении кажется дорогостоящим.
Эта проблема кажется присущей природе выполнения функционального программирования с пользовательским интерфейсом: список был обновлен, поэтому вам нужно повторно отобразить список, но, к сожалению, это очень длинный список и кажется неэффективным... С Backbone вы могли бы взломать что-то, чтобы только отнести ребенка. Даже если вы выведете этого ребенка с помощью React, вы должны вызвать рендеринг в императивном порядке, вместо того, чтобы просто объявлять "когда список изменяется, повторно отобразить его".
Решение вашей проблемы
Очевидно, что подключение элементов списка 100k кажется удобным, но не является показательным из-за вызова прослушивателей 100 k реагирования-редукта, даже если они быстры.
Теперь, если вы подключаете большой список из 100 тыс. элементов вместо отдельных элементов отдельно, вы вызываете только один прослушиватель rec-redux, а затем должны эффективно отображать этот список.
Наивное решение
Итерирование над 100 тыс. элементов для их рендеринга, что приводит к 99999 элементам, возвращающим false в shouldComponentUpdate
и одному повторному рендерингу:
list.map(item => this.renderItem(item))
Решение для исполнителей 1: пользовательский connect
+ усилитель хранения
Метод React-Redux connect
- это просто Компонент более высокого порядка (HOC), который вводит данные в обернутый компонент, Для этого он регистрирует прослушиватель store.subscribe(...)
для каждого подключенного компонента.
Если вы хотите подключить 100k элементов одного списка, это критический путь вашего приложения, который стоит оптимизировать. Вместо использования connect
по умолчанию вы можете создать свой собственный.
- Усовершенствователь магазина
Выставить дополнительный метод store.subscribeItem(itemId,listener)
Оберните dispatch
, чтобы всякий раз, когда действие, связанное с элементом, отправляется, вы вызываете зарегистрированного слушателя (ов) этого элемента.
Хорошим источником вдохновения для этой реализации может быть redux-batched-subscribe.
- Пользовательское подключение
Создайте компонент более высокого порядка с помощью API, например:
Item = connectItem(Item)
HOC может ожидать свойство itemId
. Он может использовать расширенное хранилище Redux из контекста React, а затем зарегистрировать его прослушиватель: store.subscribeItem(itemId,callback)
. Исходный код оригинала connect
может служить базовым вдохновением.
- HOC приведет только к повторному рендерингу, если элемент изменится
Связанный ответ: fooobar.com/questions/113061/...
Связанная проблема с редукцией: https://github.com/rackt/react-redux/issues/269
Выполняющее решение 2: попытки вектора
Более эффективный подход предполагает использование постоянной структуры данных, например vector trie:
Если вы представляете список ваших 100 тыс. позиций как три, каждый промежуточный node имеет возможность коротко обрезать рендеринг, что позволяет избежать большого количества shouldComponentUpdate
в дочерних элементах.
Этот метод можно использовать с ImmutableJS, и вы можете найти некоторые эксперименты, которые я сделал с ImmutableJS: Реальная производительность: рендеринг большого списка с помощью PureRenderMixin
Однако у этого есть недостатки, поскольку libs, такие как ImmutableJs, еще не предоставляют публичные/стабильные API для этого (issue), и мое решение загрязняет DOM с некоторыми бесполезными промежуточными узлами <span>
(issue).
Вот JsFiddle, который демонстрирует, как эффективный рендеринг списка объектов из 100 тысяч объектов ImmutableJS. Первоначальный рендеринг довольно длинный (но я думаю, вы не инициализируете свое приложение со 100 тыс. Элементов!), Но после того, как вы заметите, что каждое обновление приводит к небольшому количеству shouldComponentUpdate
. В моем примере я только обновляю первый элемент каждую секунду, и вы замечаете, даже если в списке есть 100 тыс. Элементов, для этого требуется только 110 вызовов shouldComponentUpdate
, что гораздо более приемлемо!:)
Изменить: кажется, что ImmutableJS не так хорош, чтобы сохранить свою неизменную структуру для некоторых операций, таких как вставка/удаление элементов в случайном индексе. Вот JsFiddle, который демонстрирует производительность, которую вы можете ожидать в соответствии с операцией в списке. Удивительно, но если вы хотите добавить много элементов в конец большого списка, вызов list.push(value)
много раз, кажется, сохранит гораздо больше структуру дерева, чем вызов list.concat(values)
.
Кстати, документировано, что List эффективен при изменении ребер. Я не думаю, что эти плохие показатели при добавлении/удалении по заданному индексу связаны с моей техникой, а скорее связаны с основной реализацией списка ImmutableJs.
Списки реализуют Deque с эффективным добавлением и удалением из конца (push, pop) и begin (unshift, shift).