Нужна помощь.
Пилится интернет-магазин на React, на странице листинга есть сайдбар и сами товары. Если сайдбар меньше в высоту, чем товары, то сайдбар скроллится, если наоборот, товары скроллятся.
Также есть баннер в хедере, который отображается только на страницах листингах, при этом в хедере есть блок, который становится фиксированным, когда до него дойдет скролл.
Скролл сделал, все работает, но беспокоит вот что:
1) В компоненте хедера повесил обработчик scroll, и получается что он отрабатывает постоянно и перерисовывает хедер. По идее ключи никакие не меняются и state меняется только когда скролл проходит отправную точку. Но React devtools выделяет красным мой хедер и componentDidUpdate отрабатывает при каждом скролле.
2) В листинге такая же проблема, при работе со скроллом React devtools показывает ререндер компонентов.
Вопросы:
1) Действительно ли реакт ререндерит эти компоненты? Или он ререндерит только обертку, у которой меняются свойства CSS, заданные через styled-components?
2) Как вообще лучше работать со скроллом? Делать через React или просто подключить отдельный JS файл и работать с DOM напрямую? Знаю, что с React так работать с DOM не нужно, но может это именно тот случай когда стоит?
По идее ключи никакие не меняются и state меняется только когда скролл проходит отправную точку. Но React devtools выделяет красным мой хедер и componentDidUpdate отрабатывает при каждом скролле.
Скорей всего, просто криво написали код и он работает не так, как вы задумывали. Постоянный вызов componentDidUpdate говорит о том, что на каждый скролл происходит перерисовка.
Действительно ли реакт ререндерит эти компоненты?
Действительно.
Как вообще лучше работать со скроллом? Делать через React или просто подключить отдельный JS файл и работать с DOM напрямую?
Все зависит от задачи. Подключать отдельный файл с нативным кодом точно не стоит.
Знаю, что с React так работать с DOM не нужно
Можно и очень часто бывает нужно. Для этого в React есть рефы.
Скорей всего, просто криво написали код и он работает не так, как вы задумывали. Постоянный вызов componentDidUpdate говорит о том, что на каждый скролл происходит перерисовка.
И Реакт же должен смотреть DOM дерево по ключам, если произошли изменения, то перерисовывать, но у меня данные не поменялись, поменялись только координаты блока обертки, почему он тогда перерисовывает вообще все?
Еще кстати, React devtools подсвечивает красным только обертку компонента, внутри этой обертки еще куча компонентов и они никаким цветом не подсвечиваются.
Вывел сообщение в консоль компонентов внутри, оно не выводится. Значит все же компоненты не перерисовываются и перерисовывается только обертка. Или я не прав?
Использовать фиксированное позиционирование. В данном случае, по сути и не нужно слушать скролл.
И Реакт же должен смотреть DOM дерево по ключам, если произошли изменения, то перерисовывать, но у меня данные не поменялись, поменялись только координаты блока обертки, почему он тогда перерисовывает вообще все?
Рендер не означает полное обновление реального DOM и не означает render всех вложенных компонентов. В DOM обновляется только обертка, но чтобы ее обновить, библиотеке надо выполнить render, сравнить две версии Virtual DOM нод и сделать необходимые обновления.
Еще для частых обновлений вроде обновления top лучше использовать атрибут style, так как Styled Compоnents для каждого значения генерирует класс.
По вашей задаче, если, допустим, у вас есть Header, который имеет два состояния, то по событию скролл его обновление должно происходить только в тот, момент когда это нужно, а не каждый вызов. То есть при скролле вниз это будет всего одно обновление. Как-то так по-хорошему должно быть. Для этого надо использовать условные операторы.
Использовать фиксированное позиционирование. В данном случае, по сути и не нужно слушать скролл.
Одним фиксированным обойтись не получится, тут сложная логика скролла двух блоков, они могут иметь разную высоту и от этого зависит их поведение. Нужно использовать все три позиционирования
По вашей задаче, если, допустим, у вас есть Header, который имеет два состояния, то по событию скролл его обновление должно происходить только в тот, момент когда это нужно, а не каждый вызов. То есть при скролле вниз это будет всего одно обновление.
Не совсем подходит, т.к. при прокрутке я могу поменять позиционирование несколько раз, ловить событие 1 раз при прокрутке в одну сторону не выйдет. Реализацию того, как мне нужно сделать можно посмотреть тут https://skybuy.ru/catalog/smesiteli/ если зайти в категорию, где сайдбар меньше в высоту, чем товары, то там поведение сайдбара меняется
Павел Диденко, я не видел ваш код) В примере в комментарии к ответу совсем простой кейс, где вообще нет смысла слушать скролл.
Реализацию того, как мне нужно сделать можно посмотреть тут https://skybuy.ru/catalog/smesiteli/ если зайти в категорию, где сайдбар меньше в высоту, чем товары, то там поведение сайдбара меняется
Как видите там состояние меняется лишь при выполнении ряда условий, а у вас, судя по рассказам, каждый скролл. Неправильно написали алгоритм.
Антон Спирин, выкладывал код в другом комментарии тут тыц у меня аналогично, состояние меняется в нескольких условиях. Это компонент-контейнер, им оборачиваю сайдбар и блок с товарами, оттуда слушаю изменненные пропсы и исходя из этих изменений управляю стилями через styled-components.
Если делаю скролл вниз, то ререндера практически нет, а вот вверх - постоянно подсвечивает
какой смысл выкладывать в песочницу нерабочий код?
Чтобы здесь простыню кода не выкладывать, там удобнее смотреть.
а это значит, что у вас не аналогично и состояние изменяется каждое событие. По-хорошему, так быть не должно.
В функции, которую вызываю в событии скролла залогировал prevState и текущий, ничего не меняется, но срабатывает DidUpdate.
Я в общем лучше сделаю полноценную песочницу и покажу
Павел Диденко, ну смотрите. В компоненте по-умолчанию нет никакой проверки соответствия состояния и вызов setState инициирует его перерисовку. Для того чтобы ее предотвратить достаточно использовать PureComponent или реализовать метод shouldComponentUpdate. Но это не самый верный путь, по-хорошему, вам надо менять алгоритм. Вызовы setState должны происходить только тогда, когда в этом есть необходимость.