Разрушение производительности приложения React через Redux

Разрушение производительности приложения React через Redux

От автора: если разработать с помощью React Redux приложение, то это упрощает управление состояниями React. Но если Redux не интегрирован правильно, это может нивелировать ключевые преимущества использования React, разрушая производительность. В этом посте описывается, как вышло так, что я создал приложение с медленными показателями, и какие шаги я предпринял для исправления ситуации.

Нашей команде было поручено создать инструмент, позволяющий пользователям добавлять и редактировать иерархию данных. Наше приложение React было хорошо настроено, пока пользователь не просматривал страницу с сотнями отображаемых компонентов. Это не должно было быть проблемой после загрузки страницы, так как React должен отображать только то, что требуется для изменений состояния. Но страница зависала на каждом изменении состояния. Что-то явно было не так.

Мы проанализировали обработку при каждом изменении состояния, используя инструменты для разработчиков Chrome для построения диаграммы пламени. Она показывает объем обработки, происходящий при каждом изменении состояния, с каждым обновлением, за считанные секунды до того, как страница снова ответит. Наш опыт был далеким от оптимального.

Проблема

Я реконструировал проблему в небольшом JSFiddle, чтобы продемонстрировать сложности, с которыми мы столкнулись в приложении, визуализируя изменения с помощью простого счетчика каждый раз, когда вызывается рендеринг. Нажатие заставляет хранилище Redux обновлять значение «clicks». «Clicks» не передаются в компонент, и ничего больше в хранилище не обновляется, так почему компонент снова отображается?

Посмотрите, можете ли вы определить, что заставляет компонент продолжать рендеринг и вызывает проблемы с производительностью.

Причина

Фильтр в mapStateToProps был причиной множественных рендерингов, и производительность падала. Позволь мне объяснить…

При каждом изменении состояния, mapStateToProps запускается, чтобы проверить, что компонент обновлен с изменением. Фильтр удаляет любой из emoji, который не должен отображаться, и передает их в свойства компонента в новом массиве. Затем React проверяет, изменились ли свойства, и, если да, он снова отображает компонент. Поскольку фильтр всегда возвращает новый массив, компонент всегда будет отображаться для КАЖДОГО изменения состояния.

Я немного сгущаю краски, но все, что создает новый экземпляр чего-либо в mapStateToProps, будет демонстрировать то же поведение!

Приложение постоянно содержало на странице 100 отдельных компонентов, используя mapStateToProps с фильтром или картой, и это заставляло их повторно визуализироваться для каждого изменения состояния. Не идеально.

Решение для сравнения состояний

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

Хотя это решение позволяет справиться с проблемами производительности, оно не является удовлетворительным. Это означает, что компонент теперь будет имеет дело не только с отображением свойства и должен знать больше о данных, которые передаются ему, так как он выполняет фильтрацию. К счастью, есть лучший способ!

Покопавшись в различных источниках по этой теме, я нашел волшебное решение Redux для этой проблемы, которое хорошо скрыто в их документации, судя по количеству постов о «неизвестных приемах улучшения производительности».

Функция connect позволяет передавать дополнительные аргументы, чтобы настроить способ обновления компонента. Параметр isStatesEqual позволяет разработчикам переопределять, как проверяется состояние.

Это позволяет компоненту оставаться неизменным из первоначальной реализации, а подключенный компонент будет перевизуализироваться только после того, как произойдут соответствующие изменения. В приведенном выше примере, если хранилище emoji не изменилось, mapStateToProp не запускается. Поэтому новые экземпляры не создаются, и React не визуализирует их повторно. Задача решена!

React и Redux отлично сочетаются, но если вы не будете осторожны с реализацией, можно очень просто снизить производительность приложения, создавая медленный пользовательский интерфейс.

Автор: Hugh Ashby-Hayter

Источник: https://blog.scottlogic.com/

Редакция: Команда webformyself.