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

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

Не торопитесь!