当我导航到新页面时,Reach Router 会向下滚动到页眉之后的页面内容(如果内容足够长)。我假设这是为了可访问性,但对我的应用程序来说不是必需的,而且它实际上很不和谐。可以禁用此行为吗?
请注意,我说的是 Reach Router 而不是 React Router。
当我导航到新页面时,Reach Router 会向下滚动到页眉之后的页面内容(如果内容足够长)。我假设这是为了可访问性,但对我的应用程序来说不是必需的,而且它实际上很不和谐。可以禁用此行为吗?
请注意,我说的是 Reach Router 而不是 React Router。
尝试使用<Router primary={false}>
which 不会专注于路由组件。
https://reach.tech/router/api/Router
主要:布尔
默认为真。主路由器将管理对位置转换的关注。如果为 false,则不会管理焦点。这对于呈现为边栏、标题、面包屑等但不是主要内容的路由器很有用。
警告:如果您担心破坏可访问性,请参阅此答案:https ://stackoverflow.com/a/56996986/428780
这里的最佳答案虽然解决了 OP 的问题,但可能不是大多数人想要的解决方案,因为它关闭了 Reach 路由器最重要的可访问性功能。
事实上,Reach 路由器将匹配的内容集中在<Route>
路由更改上是出于可访问性的原因- 因此,当您导航到新页面时,屏幕阅读器等可以被定向到新更新的相关内容。
它用于HTMLElement.focus()
执行此操作 -请参阅此处的 MDN 文档。
问题是默认情况下,这个函数会滚动到被聚焦的元素。有一个preventScroll
参数可以用来关闭这个行为,但是浏览器对它的支持不好,不管怎样,Reach Router 不使用它。
设置会为您可能拥有的任何嵌套primary={false}
关闭此行为- 它不打算在您的主(主要)上设置- 因此名称。 <Router>
false
<Router>
因此,正如最佳答案所暗示的那样,primary={false}
在您的 primary 上设置<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 的滚动恢复指南。
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
}
我用它制作了一个 npm 包以简化集成:https ://www.npmjs.com/package/reach-router-scroll-top
使用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 中是否有类似的解决方案,但该解决方案会更优化。
一定是有别的原因造成的。就像@Vinicius 说的那样。因为对于我的应用程序primary={false}
确实有效。我有一个小应用程序和下面的路线。
<Router primary={false}>
<Home path="/" />
<Dashboard path="dashboard" />
<Users path="users" />
<Sales path="sales" />
<Settings path="settings" />
</Router>