Оптимизация компонентов Redux
В первой части трехнедельной серии статей Riipen о том, как оптимизировать компоненты React, мы исследовали виртуальную модель DOM React и антипаттерны, которые портят процесс согласования.
На этой неделе мы рассмотрим, как можно вообще избежать этого процесса, если вы уже знаете, что ваш компонент не нуждается в повторной визуализации (например, если его вывод изменяется только при изменении определенных свойств). Мы также узнаем, как применить то, что мы узнали на прошлой неделе, к компонентам, подключенным к магазину Redux.
Оптимизация под Redux
После того, как вы получили рендеринг компонента без создания избыточных ссылок на объекты, следующим шагом в оптимизации является рассмотрение того, что происходит в вашем компоненте до его рендеринга, то есть до того, как React получит возможность согласовать изменения. Этот код может запускаться каждый раз при обновлении состояния, даже если ваш компонент возвращает дерево компонентов, которое можно легко согласовать (без ошибок, упомянутых в нашем предыдущем посте).
Прежде чем мы углубимся, взгляните на документацию React, чтобы ознакомиться с жизненным циклом компонента.
Те же правила применяются к функциям отображения состояний
Если вы используете Redux, помните, что делают подключенные компоненты до их рендеринга. Функции сопоставления, такие как mapStateToProps
, могут выполнять большую работу каждый раз при обновлении состояния, даже если React в конечном итоге отбрасывает выходные данные рендеринга подключенного компонента.
Как и в случае с render
, избегайте создания обратных вызовов и других функций в функциях сопоставления, особенно если эти переменные будут переданы в качестве свойств другим компонентам. Вместо этого используйте mapDispatchToProps
или воспользуйтесь тем фактом, что по умолчанию connect
передает dispatch
вашему компоненту в качестве опоры. Это означает, что вы можете создавать методы компонентов, которые используют this.props.dispatch
как функцию, вместо того, чтобы создавать новые функции каждый раз при изменении состояния - что может происходить довольно часто, особенно если вы используете такой пакет, как Redux Form.
No changes? Skip Rendering Altogether
В некоторых случаях, даже если React может отбрасывать вывод render
большую часть времени, метод все равно выполняет много ненужной работы. Вы можете полностью пропустить рендеринг, реализовав shouldComponentUpdate
. Это простой метод, который принимает nextProps
и nextState
в качестве аргументов и возвращает true
, если нужно вызвать render
. Если он возвращает false
, React не будет отображать компонент или согласовывать результат с тем, что уже было отображено.
В большинстве случаев в приложении Redux shouldComponentUpdate
сравнивает только nextProps
, потому что любые изменения состояния переменных уже были выбраны для компонента в mapStateToProps
.
Лучше всего реализовывать shouldComponentUpdate
только в том случае, если вы очень уверены в условиях, при которых ваш компонент должен отображаться. Например, если вы выводите список объектов и знаете, что ваш компонент изменяется только тогда, когда они изменяются в хранилище Redux, вы можете реализовать этот метод с уверенностью, что ваш компонент не устареет из-за других изменений состояния.
См. Сообщение в блоге Франсуа Занинотто на Marmelab, где есть отличный пример рефакторинга компонентов для эффективного использования shouldComponentUpdate
.
Советы
- Ваша реализация должна быть быстрой, в идеале - проводить прямое сравнение определенных свойств, которые, как вы знаете, являются переменными; в противном случае вы можете застопорить компонент в своих усилиях по его оптимизации!
- Хорошее практическое правило состоит в том, чтобы ваша реализация была не более сложной, чем
PureComponent.shouldComponentUpdate
, который выполняет поверхностное сравнениеstate
иprops
(см. Ниже). - Если ваш компонент может использовать
shouldComponentUpdate
, перенесите как можно больше логики в функцию рендеринга, чтобы этого можно было избежать, если компонент не нуждается в обновлении. - Имейте в виду, что будущие версии React могут интерпретировать результаты
shouldComponentUpdate
как подсказку, а не директиву.
Consider React.PureComponent
В то время как React.Component
реализует метод по умолчанию shouldComponentUpdate
, который всегда возвращает true
, PureComponent
реализует неглубокое сравнение свойств и состояний. Это может быть ярлык для пользовательской реализации shouldComponentUpdate
, но применяются те же правила, что и для общего согласования: mapStateToProps
и mapDispatchToProps
могут легко помешать сравнению, передавая новые ссылки на одни и те же данные при каждом вызове.
Тем не менее, использование библиотеки наподобие Reselect, которая запоминает селекторы Redux, предотвратит создание новых ссылок из простого выбора данных.
Как правило, использование PureComponent
должно быть завершением вашей оптимизации, а не отправной точкой. По умолчанию это действительно замедлит ваше приложение, потому что React может в конечном итоге выполнять двойное сравнение при каждом рендеринге: сначала props
и state
, а затем (если они разные) виртуального DOM, возвращаемого render
.
Если вы решите расширить PureComponent
, имейте четкое представление о том, какие изменения свойств и состояний вам нужно учитывать и почему пользовательская реализация shouldComponentUpdate
будет избыточной.
Пропустить сопоставление свойств Redux
Подобно тому, как React позволяет пропускать рендеринг с shouldComponentUpdate
, React Redux позволяет пропускать mapStateToProps
и mapDispatchToProps
. Декоратор connect
принимает options
в качестве четвертого параметра, который представляет собой объект, который может иметь следующие свойства:
areStatesEqual(next, prev)
areOwnPropsEqual(next, prev)
areStatePropsEqual(next, prev)
areMergedPropsEqual(next, prev)
Каждая функция, конечно, необязательна и просто возвращает true
или false
. См. Документацию React Redux для получения подробной информации о том, как использовать эти параметры.
Если ваша mapStateToProps
функция выполняет что-то более сложное, чем просто выбор данных из магазина, и которую вы не можете перенести в функцию render
, рассмотрите возможность реализации одного из этих тестов. Однако в подавляющем большинстве случаев в этом нет необходимости.
Хотя поначалу оптимизация приложений Redux может показаться сложной, к ним применяются те же принципы, что и к обычным приложениям React. Понимание того, что происходит за кулисами, позволяет помнить о нескольких простых вещах. И при разумном использовании таких хуков, как shouldComponentUpdate
, вы всегда можете обойти поведение по умолчанию, чтобы ускорить более сложный код.
В заключительной части этой серии, опубликованной на следующей неделе, мы рассмотрим некоторые инструменты, которые вы можете использовать, чтобы определить, какие компоненты в вашем приложении больше всего выиграют от этих методов, и как вы можете измерить эффективность своих оптимизаций.
Не торопитесь!