Остановите прокрутку Reach Router вниз по странице после перехода на новую страницу
Когда я перехожу на новую страницу, Reach Router прокручивает содержимое страницы за заголовком (если содержимое достаточно длинное). Я предполагаю, что это для доступности, но это не обязательно для моего приложения, и это на самом деле довольно неприятно. Можно ли отключить это поведение?
Обратите внимание, что я говорю о Reach Router, а не React Router.
1 ответ
Попробуйте использовать <Router primary={false}>
который не будет фокусироваться на компоненте маршрута.
https://reach.tech/router/api/Router
основной: bool
По умолчанию true. Первичные маршрутизаторы будут управлять фокусом на переходах местоположения. Если false, фокус не будет управляться. Это полезно для маршрутизаторов, отображаемых в виде сторон, заголовков, хлебных крошек и т. Д., Но не для основного содержимого.
Главный ответ здесь, при решении проблемы OP, вероятно, не является решением, которое нужно большинству людей, поскольку он отключает наиболее важную функцию доступности маршрутизатора Reach.
Тот факт, что маршрутизатор Reach фокусируется на контенте <Route>
изменение маршрута происходит по причинам доступности - поэтому программы чтения с экрана и т. д. могут быть направлены на недавно обновленный релевантный контент при переходе на новую страницу.
Оно использует HTMLElement.focus()
чтобы сделать это - см. MDN документы здесь.
Проблема в том, что по умолчанию эта функция прокручивается до фокусируемого элемента. Есть preventScroll
Аргумент, который можно использовать, чтобы отключить это поведение, но поддержка браузера для него не очень хороша, и независимо от этого, Reach Router не использует его.
primary
поддержать Router
используется, чтобы отключить это поведение для любого вложенного <Router>
у вас может быть - он не предназначен для использования на вашем основном (основном) <Router>
- отсюда и название.
Установка этого false
на вашем основном <Router>
Как показывает верхний ответ, "работает" в том смысле, что он останавливает прокрутку, но достигает этого, просто полностью отключая фокусировку, что нарушает функцию доступности. Как я уже сказал, если вы сделаете это, вы нарушите одну из основных причин использования Reach Router.
Итак, каково решение?
В основном, кажется, что этот побочный эффект HTMLElement.focus()
- прокрутка к сфокусированному элементу - неизбежна. Поэтому, если вам нужна функция доступности, вы должны использовать прокрутку.
Но с учетом сказанного может быть обходной путь. Если вы вручную прокрутите страницу вверх, используя window.scrollTo(0, 0)
Я полагаю, что при каждом изменении маршрута это не "нарушит" функцию фокусировки с точки зрения доступности, но "исправит" поведение прокрутки с точки зрения UX.
Конечно, это немного хакерский и обязательный обходной путь, но я думаю, что это лучшее (может быть, единственное) решение этой проблемы без нарушения доступности.
Вот как я это реализовал
class OnRouteChangeWorker extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.location.pathname !== prevProps.location.pathname) {
this.props.action()
}
}
render() {
return null
}
}
const OnRouteChange = ({ action }) => (
{/*
Location is an import from @reach/router,
provides current location from context
*/}
<Location>
{({ location }) => <OnRouteChangeWorker location={location} action={action} />}
</Location>
)
const Routes = () => (
<>
<Router>
<LayoutWithHeaderBar path="/">
<Home path="/" />
<Foo path="/foo" />
<Bar path="/bar" />
</LayoutWithHeaderBar>
</Router>
{/*
must come *after* <Router> else Reach router will call focus()
on the matched route after action is called, undoing the behaviour!
*/}
<OnRouteChange action={() => { window.scrollTo(0, 0) } />
</>
)
Построив ответ @Marcus, вы можете избавиться от рывка с useLayoutEffect()
вместо useEffect()
- таким образом, действие прокрутки происходит после полного рендеринга DOM, так что вы не получите странного "отскока".
// ScrollToTop.js
import React from 'react'
export const ScrollToTop = ({ children, location }) => {
React.useLayoutEffect(() => window.scrollTo(0, 0), [location.pathname])
return children
}
Я должен был использовать комбинацию вещей, чтобы заставить это работать. Даже с настройкой primary={false}
все еще были случаи, когда страницы не прокручивались до самого верха.
<Router primary={false}>
<ScrollToTop path="/">
<Home path="/" />
<Contact path="contact-us" />
<ThankYou path="thank-you" />
<WhoWeAre path="who-we-are" />
</ScrollToTop>
</Router>
Это основано на руководстве по восстановлению свитков React Router.
Компонент Scroll to Top будет работать с вами, у вас нет primary={false}
, но это вызывает рывок из того места, где он фокусирует маршрут и затем вызывает window.scrollTo
,
// ScrollToTop.js
import React from 'react'
export const ScrollToTop = ({ children, location }) => {
React.useEffect(() => window.scrollTo(0, 0), [location.pathname])
return children
}
Должно быть что-то еще, чтобы вызвать это. Как сказал @Vinicius. Потому что для моего приложения primary={false}
действительно работает. У меня есть небольшое приложение и мои маршруты ниже.
<Router primary={false}>
<Home path="/" />
<Dashboard path="dashboard" />
<Users path="users" />
<Sales path="sales" />
<Settings path="settings" />
</Router>
С помощью primary={false}
не решает все дела. Распространенное решение - обернуть страницу и использоватьuseEffect
для достижения результата. Как заметил кто-то, может быть проблема со вспышкой, хотя со мной этого никогда не случалось, так что испытайте удачу.
<PageWrapper Component={About} path="/about" />
const PageWrapper = ({ path, Component }) => {
useEffect(() => {
window.scrollTo(0, 0)
}, [path])
return <Component path={path} />
}
Обратите внимание, что в React Router вы можете использовать history.listen
как объяснено здесь. Я не проверял, есть ли подобное решение в Reach Router, но это решение было бы более оптимальным.
Я сделал из него пакет npm, чтобы упростить интеграцию: https://www.npmjs.com/package/reach-router-scroll-top