Всем привет! Я фуллстек, пишу код для сервиса TolstoyComments. В процессе работы был накоплен ценный опыт, как решать разного рода задачи на React. Этим опытом я хочу поделиться. Бывало так, что после загрузки страницы, вам нужно сделать прокрутку экрана к заданному месту? Все ли всегда получалось с первого раза? И что делать если скролл все равно дергается в процессе загрузки страницы? Это пример самой комментируемой статьи на хабре когда скролл не с первого раза показывает нужный комментарий В примере я хотел показать, что даже на хабре это работает не с первого раза. Видно, что после инициализации страницы, нужный комментарий оказался в нужном месте, потом он куда-то улетел, а потом снова оказался в нужном месте. Все дело в высоте содержимого над комментарием! Если в процессе отрисовки страницы изменится высота содержимого над комментарием, то позиционирование скролла будет потеряно. Разберем более детально пример в начале статьи. Сначала загружается HTML и делается доскролл до нужного комментария. Потом скролл теряется, так как высота содержимого над комментарием изменилась и скролл уже показывает не туда куда надо. После этого происходит коррекция скролла и он снова начинает показывать куда надо. Здесь я делаю предположение, что возможно разработчики хабра это предусмотрели и на всякий случай сделали костыль, который срабатывает как раз в том случае если скролл все таки теряется. Всему виной этот тег:<img />! (но не всегда!) Самый простой случай, когда содержимое верстки может менять свою высоту связано с выводом картинок. Картинки в верстке имеют ширину и высоту и чаще всего их вывод делается адаптивным за счет css стилей. Чаще всего пользуются вот таким сочетанием стилей: Это магическая конструкция заставляет браузеры пропорционально масштабировать изображение, под разную ширину экрана. Но чтобы она корректно работала нужно выполнить одно условие: нужно чтобы в теге img были прописаны исходные размеры изображения: ширина и высота (параметры width и height). Взглянем на верстку картинок в комментариях на Хабре: Как можно видеть, атрибутов width и height тут нет, а значить браузер не сможет корректно определить высоту содержимого страницы до того, как начнется загрузка самой картинки. Получается, чтобы обойти проблему с выводом картинок нужно: Либо заранее прописывать корректные значения width и height Либо вешать на картинку события onload onerror и обрабатывать эти события чтобы узнать о том когда картинка загрузится или не загрузится. А по другому никак? Да конечно, есть еще вариант как сделано на хабре, я кстати тоже так сделал при разработке комментариев на портале myslo.ru (пример ссылки на комментарий и отработки доскролла) setTimeout и setInterval - решение на все случаи жизни! С помощью этих функций происходит повторная докрутка скролла если с первого раза что-то пошло не так. Конечно это приводит к некрасивым сайд эффектам на странице. Все просто, нужно избавиться от динамического контента! Шучу конечно, на практике такое невозможно и это нужно как-то решать. Самый простой пример реализации скролла до нужного элемента на react: Есть некоторый компонент, после инициализации которого происходит вызов useLayoutEffect и перемещение скролла. В этом примере у нас нет элементов, которые могут менять свою высоту в процессе рендеринга страницы. Поэтому доскролл будет работать корректно. Пример доскролла до элемента на codesandbox. Теперь добавим немного динамического контента Пример с подгрузкой картинок на codesandbox Если внимательно посмотреть что будет происходить в этом примере то можно заметить как скролл сразу отображается на нужном месте но полоса скролла как-то странно уменьшается. Что же тут происходит? Сначала происходит полная загрузка HTML. Потом браузер делает фокус скролла на нужном нам элементе. А потом происходит подгрузка картинок выше места скролла. Но поскольку все современные браузеры умные, то они автоматически двигает скролл так чтобы содержимое не смещалось относительно левого верхнего угла.  Браузер пытается любой ценой сохранить нужное нам положение скролла. Иногда у него это получается, иногда нет. В случае использования scrollintoview браузер понимает на каком элементе нужно сохранить положение скролла до полной загрузки страницы, и поэтому положение скролла не теряется. Но что делать если у нас SPA и мы хотим реализовать восстановление положения скролла при переходе на шаг назад? В этом случае, проблема в том что мы не можем использовать метод scrollintoview. Мы можем использовать window.scrollTo но этот метод уже не имеет такого умного поведения сохранения положения скролла. Для удобства я сделал вывод числового значения положения скролла и записал процесс обновления страницы в замедленном виде. Страница загружается, потом происходит перемещение скролла на 1000 (как и нужно). Но потом скролл становится 1294 и даже 1589. Откуда такие значения скролла? Исходный код: Пример с выводом текущего положения скролла, решенный через костыль codesandbox Разберем написанный код: Эта строка должна восстанавливать положение скролла, но она не работает, так как на странице есть динамические компоненты с изменяемой высотой. Эта строка - костыль, призванный решить проблемы, которые может не решить строка выше. Здесь весь вопрос в том какой нужно выставить таймер. А если у вас медленный интернет и страница за 2 секунды целиком не загрузится? Или здесь можно двигать скролл постоянно пока пользователь не начнет сам двигать скролл.  Реализация подобного костыля может быть разной, и может быть сложной, но проблема которую этот костыль должен решать одна - сделать правильное позиционирование скролла после загрузки всей страницы.  Скролл дергается в процессе отрисовки страницы и это мягко говоря не красиво, а грубо говоря раздражает. На этом месте уже можно сформулировать задачу: Дано, страница с динамическими элементами, которые могут менять свою высоту в процессе отрисовки. Нужно реализовать такой метод onRenderChildrenComplete чтобы он вызывался сразу после завершения отрисовки всех дочерних компонентов. Самое просто решение - это реализовать во всех динамических компонентах событие onRenderComplete и передавать его через props. Это событие будет вызываться после полной отрисовки компонента. Но на практике можно столкнуться с множеством проблем так как в примере выше динамических компонентов может быть несколько.  А если динамический компонент добавлен через условие? Здесь могут возникнуть сложности. Формулируя выше описанные требования я решил все реализовать через контекст: Здесь описана реализация заглушки для контекста. Компонент RenderContextProvider принимает параметр onRender (сокращение от onRenderChildrenComplete)  - событие завершения рендеринга всех дочерних компонентов. useLayoutRender - хук который нужно вызывать в динамическом компоненте. Этот хук вернет метод onRender - событие завершения рендеринга.  После подключения хука в нем будет вызван инкримент счетчика countInit. А после завершения рендеринга в компоненте будет вызвано событие onRender и тем самым значения счетчика countRender совпадет со значением счетчика countInit. При совпадении этих значений произойдет вызова события onRenderChildrenComplete. Пример реализации: Финальный результат: После загрузке всех картинок на странице происходит вызов метода, в котором запускается позиционирование скролла.  Конечно в реальных проектах могут быть совершенно разные динамические компоненты, намного сложнее приведенного примера с картинками. Здесь так же может идти речь о компонентах, которые нуждается в дополнительном fetch. Но тем не менее, описанный выше способ позволяет решить и эти проблемы. Контролировать положение скролла непросто. Велика вероятность получить сайд эффект или потерю положения скролла если делать позиционирование не в нужный момент. В мире React эту проблему можно решить и получить возможность корректного восстановления скролла для SPA сайтов. Пример в статье с картинками явно показывает что даже в случае нескольких динамических компонентов, событие завершения рендеринга всех дочерних компонентов отрабатывает корректно. Приведенный мною код не решает всех проблем динамического рендеринга. Здесь по-прежнему остается просто поле скрытых нюансов, каждый из которых характерен для конкретной задачи. web-программист