Проблемы, с которыми столкнулся, когда разрабатывал свой чат

План: 1) Введение 2) Рабочий сценарий, из-за которого получилось обнаружить проблему 3) Intersection observer и готовые решения, которые основаны на событии скрола, ресайза и мыши, проблема кеша и...

План:

1) Введение

2) Рабочий сценарий, из-за которого получилось обнаружить проблему

3) Intersection observer и готовые решения, которые основаны на событии скрола, ресайза и мыши, проблема кеша и бесконечного скрола

5) Выводы

Введение

Всем привет, меня зовут Александр, я являюсь фронтенд разработчиком более 4-х лет. В этой статье хочу поделится с вами проблемами, с которыми я столкнулся, когда занимался разработкой своего чата для закрепления понимания работы с сокетами. Также во время работы с чатом я познакомился с технологией intersection observer и обкатывал работу rtk query для себя, зайдет или нет.

Рабочий сценарий, из-за которого получилось обнаружить проблему

Когда я начал работать с сообщениями и прописывать их на стороне юая, то столкнулся с тем, что у меня не получалось сделать бесконечную загрузку при прокрутке вверх. То есть я прописывал условие для вызова функции, саму функцию загрузки и отрисовки данных, но после этого список некорректно перерисовывался и происходил бесконечный запрос из-за того, что не было перемещения вниз к тому сообщению, перед которым была вызвана подгрузка данных. Ранее в своей статье «Получение списка пользователей и их вывод на ui» я упоминал, что бесконечную подгрузку с rtk query у меня не получилось организовать. Давайте теперь подробней разберем эту проблему и как ее можно решить.

Фрагмент чата с моего проекта
Фрагмент чата с моего проекта

Для более глубокого понимания приведу наглядный пример, на картинке выше отображен фрагмент чата с моего сайта. Теперь давайте представим, что загрузились сообщения при первой отрисовке, вот с этого момента и начинаются проблемы. Если в кеше в этот момент есть данные по этому запросу — скрол к последнему сообщению не будет выполнен, потому что сразу будет загружена еще одна порция сообщений из кеша. Я думаю это связано с тем, что логика скрола не успевает отработать к моменту окончания загрузки первого запроса, а т. к. в кеше имеются данные для второго запроса — то эти данные сразу же и выводятся.

Intersection observer и готовые решения, которые основаны на событии скрола, ресайза и мыши, проблема кеша и бесконечного скрола

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

Ранее этот вопрос решался с помощью функции scrollTo. В этот раз я решил воспользоваться двумя разработками браузерного апи: вышеупомянутый intersection observer и scrollIntroView. Первое — это браузерное апи, которое позволяет асинхронно отслеживать пересечение элемента с его родителями или областью видимости документа, так называемый viewport. Второе — это функция, которая прокручивает контейнер родителя элемента так, чтобы элемент, на котором была вызвана упомянутая функция, стал виден пользователю.

Алгоритм имел следующий вид: на контейнер со списком сообщений вешаем наблюдателя (intersection observer) и через него в колбеке ссылку на последний элемент списка. На него вешаем функцию скрола и далее после рендера происходит скрол. Для работы с intersection observer в react есть пакет c хуком, через него и вешается наблюдатель.

Теперь перейдем к загрузке данных по скролу. Изначально для реализации бесконечного скрола я хотел использовать также intersection observer вместо готовых библиотек на базе слушателей событий, в этом случае я выигрывал по производительности. Алгоритм был следующий: пользователь выбирает активного собеседника, прогружаются сообщения и скролим к последнему сообщению. Далее пользователь прокручивает историю чата наверх и загружает новые сообщения. Вот на этом моменте у меня возникла проблема с intersection observer. Весь алгоритм работал корректно до момента повторного рендера после загрузки новых данных, а далее он поломался. Проблема оказалась в самом рендере react.

Здесь необходимо немного окунутся в теорию react для полного понимания картины. После того, как обновляется список сообщений в redux, то компонент, который использует эти данные сразу начинает перерисовку. Весь список перерисовывается заново. И в этом месте нужно быть очень внимательным. Прежде чем продолжу дальше хочу задать один вопрос: почему при бесконечном скролле вниз не нужно вешать события на скрол window или другой контейнер, а при скролле вверх это жизненно необходимо? Отвечаю на свой вопрос: при прокрутке вниз скрол сохраняет свою позицию в браузере и новые данные прогружаются вниз, в этом случае весь процесс рассчета берет на себя бразер и ничего делать не нужно. Когда подгрузка информации идет по скроллу вверх — необходимо мало того, что сначала, отрисовать данные, так еще и плавно рассчитать высоту и положение скролла на странице самостоятельно. Что я хотел сказать вышеописанным? То что после получения данных страница сначала перерисовывалась, расчета для новой позиции скрола не производилось и по этой причине старые сообщения сдвигались вниз новыми. Помимо этого обновлялась ссылка на intersection observer и в этом месте происходило замыкание — данные загружались, страница перерисовывалась и так по бесконечному кругу.

В итоге было принято решение отказаться от intersection observer как в использовании бесконечной прогрузки так и в прокрутке к последнему сообщению. Для решения данного вопроса я решил, что следует обратится к готовым решениям, которые основаны на событиях. В данном случае мне помог пакет «react-infinite-scroller», его подробную работу можете почитать в документации.

После того, как избавился от intersection observer при загрузке данных по скроллу вверх у меня появилась другая проблема — это кеширование rtk query. В этот раз проблема себя проявила по следующему алгоритму: пользователь выбирал активного собеседника и загружал их переписку, после этого переходит к другому собеседнику. Затем сразу возвращается к первому собеседнику. Если в этот момент кеш в rtk query еще актуален, то сначала происходит запрос первой части истории, а затем второй — по итогу не работал скрол к последнему сообщению в истории. Данную проблему поднимал, когда описывал изображение «Фрагмент чата с моего проекта». Для ее решения пробовал найти опцию отключения кеширования, но таковой не нашел в библиотеке, установка времени жизни кеширования в 0 также не помогла.

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

Выводы

В этой статье было разобраны проблемы бесконечного скрола вверх и rtk query. Если подводить итоги, то для загрузки по скроллу вверх лучше использовать еще старые готовые решения, основанные на слушателях событий, это более ресурсоемко, зато надежно. Intersection observer можно использовать для стандартного бесконечного скрола вниз, также ее можно использовать и в других решениях, которые основаны для отслеживания видимости компонента. Для скрола в контейнере функция scrollIntoView можно уже использовать. По поводу rtk query, само по себе решение очень хорошее и требует еще обкатки, но в узкоспециализированных ситуациях, которые встречаются редко, очень подводит.

Больше статей в моем блоге. Спасибо, что дочитали и до новых встреч в следующих статьях.