Работа со скроллом в приложениях на React с использованием React Router требует особого внимания, особенно когда речь идёт о вложенных маршрутах. По умолчанию браузер сохраняет позицию скролла при навигации между страницами, что может приводить к некорректному поведению интерфейса, если требуется сбросить или управлять скроллом программно.
В React Router версии 6 управление скроллом не встроено напрямую,
поэтому нужно создавать собственные компоненты или хуки для отслеживания
изменений маршрута и управления позицией прокрутки. Основная идея
заключается в использовании хука
useLocation, который возвращает текущий объект
маршрута:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
Компонент ScrollToTop помещается в корень приложения,
чтобы каждый переход по маршрутам сбрасывал скролл страницы в верхнюю
точку.
Вложенные маршруты добавляют сложности: если на странице есть
дочерние маршруты, их контент может загружаться динамически, и простое
использование window.scrollTo может не работать корректно.
Для таких случаев можно управлять скроллом внутри контейнеров с
прокруткой:
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
function NestedScrollContainer({ children }) {
const containerRef = useRef(null);
const { pathname } = useLocation();
useEffect(() => {
if (containerRef.current) {
containerRef.current.scrollTop = 0;
}
}, [pathname]);
return (
<div ref={containerRef} style={{ overflowY: 'auto', height: '100%' }}>
{children}
</div>
);
}
Здесь создаётся отдельный контейнер с вертикальной прокруткой, который отслеживает изменения маршрута и сбрасывает позицию скролла при навигации. Это особенно полезно в приложениях с панелями и вложенными страницами, где не требуется сбрасывать скролл всего окна браузера.
Для навигации к определённым элементам на странице удобно использовать якоря и управлять скроллом программно:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function ScrollToHash() {
const { hash } = useLocation();
useEffect(() => {
if (hash) {
const element = document.querySelector(hash);
if (element) {
element.scrollIntoView({ beh * avior: 'smooth' });
}
}
}, [hash]);
return null;
}
Компонент ScrollToHash автоматически прокручивает
страницу к элементу, чей идентификатор совпадает с хэшем маршрута
(#section1), с плавной анимацией.
В больших приложениях иногда требуется сохранять позицию скролла при возврате на предыдущую страницу. Для этого создаётся специальный хук:
import { useRef, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function useScrollRestoration() {
const { pathname } = useLocation();
const positions = useRef({});
useEffect(() => {
if (positions.current[pathname]) {
window.scrollTo(0, positions.current[pathname]);
} else {
window.scrollTo(0, 0);
}
const savePosition = () => {
positions.current[pathname] = window.scrollY;
};
window.addEventListener('beforeunload', savePosition);
return () => {
savePosition();
window.removeEventListener('beforeunload', savePosition);
};
}, [pathname]);
}
Этот подход позволяет запоминать позицию скролла для каждого маршрута и восстанавливать её при возврате, создавая ощущение «естественной» навигации.
scrollIntoView({ beh * avior: 'smooth' }).Эти подходы обеспечивают корректную работу скролла в сложных интерфейсах с вложенной маршрутизацией, улучшая пользовательский опыт и предотвращая неожиданное поведение при переходах между страницами.