На днях разговаривая с коллегами и друзьями осознал что далеко не все понимают как работают
connect и useSelector и почему даже с реселектом перфоманс может просесть.Так вот,
mapStateToProps из connect и селектор из useSelector вызываются при каждом изменении вашего стора, вообще при каждом изменении, без исключений. Если вы используете connect или useSelector только в рутовых компонентах (обычно самые верхние компоненты в дереве, например компоненты роутов) или модулях (компоненты который конектятся к стору но используется на странице всего несколько раз), то скорее всего у вас все ОК. Да будет гемор с проп-дрилингом (прокидывание пропсов в глубь дерева), но об этом ниже. Ну а если вы юзаете connect или useSelector везде где только можно, то могут быть проблемы с перфом. Во первых - в достаточно больших проектах на одной странице может быть несколько тысяч компонентов которые используют коннект/селект хук, и все селекторы внутри этих компонентов будут вызываться при каждом изменении стора. Да, с использованием
memo или connect, все компоненты перерисовываться не будут, только те, для которых данные реально поменялись, но это делает реакт, а не redux или reselct. Представьте что у вас реалтайм приложение, экшены приходят по сокетам, несколько(а может 10+) раз в секунду, так вот на каждый такой экшен все видимые селекторы (которые используется в отрендереных комонентах) будут запущены. А если реалтайм это не про меня, возразите вы, то вот пример с когда-то популярной либой redux-form , где на каждое нажатие клавише при вводе в инпут летит экшен в стор и опять эти тысячи селекторов будут запущены, на каждое нажатие клавиши, НА КАЖДОЕ КАРЛ...Во вторых -
connect, в отличает от useSelector, гарантирует что у родительских компонентов mapStateToProps будет вызван раньше чем у дочерних, это нужно что бы пофиксить "stale props" и "Zombie child" ([https://react-redux.js.org/api/hooks#stale-props-and-zombie-children](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children)). Эта гарантия добавляет экстра логику и кучу вложенных провайдеров, что так-же влияет на перф при ооочень большой вложенности конектов. В добавок connect под капотом юзает PureComponnet и оптимизирует ререндеры, но во многих ситуациях это излишне. Например все с тем-же redux-form - зачем нам сравнивать пропсы, если мы и так знаем что компонент будет перерисован при каждом нажатии клавиши(читай изменении пропса).Все это может казаться незначительным, но в больших проектах такие мелочи вытекают в реальные проблемы и приходится придумывать какие-то решение, а иногда и хаки, т.к. съехать с редакса не так-то просто 😢.
Одно из простых и довольно быстрых решений, которое так-же решает проблемы с проп-дрилингом - использовать контекст для всего, что меняется редко, но юзается часто. Я не говорю хранить значение только в контексте, можно использовать контекст как механизм доставки данных из редакса в глубь дерева. Например цветовая тема, или локаль браузера, или текущий юзер, все эти данные меняются очень редко, но могут использоваться сотнями компонентов. Если тянуть эти данные из редакса прямо в компонентах - будет connect-hell. Но можно создать отдельные контексты для темы, локали, и т.д., обернуть апку в эти контексты, пробросить им данные из редакса, а в компонентах юзать только контекст. Таким образом вместо сотен/тысяч конектов мы имеем только 1, там где данные тянуться из редакса и пробрасываются в контекст, а компоненты будут обновляться автоматически при изменении значений в контексте самим реактом.
Продолжение в первом коменте...
Читать в ноушен: https://bit.ly/3zdmFgL
#react #redux