这里的最佳答案虽然解决了 OP 的问题,但可能不是大多数人想要的解决方案,因为它关闭了 Reach 路由器最重要的可访问性功能。
事实上,Reach 路由器将匹配的<Route> 的内容集中在路由更改上是出于可访问性原因 - 因此,当您导航时,屏幕阅读器等可以被定向到新更新的相关内容到一个新的页面。
它使用HTMLElement.focus() 来执行此操作 - see the MDN docs here。
问题是默认情况下,这个函数会滚动到被聚焦的元素。 有一个preventScroll 参数可以用来关闭这个行为,但是浏览器对它的支持不好,不管怎样,Reach Router 不使用它。
设置primary={false} 会为您可能拥有的任何嵌套 <Router> 关闭此行为 - 它不打算在您的主(主要)上设置false <Router>——因此得名。
因此,正如最佳答案所暗示的那样,在您的主要 <Router> 上设置 primary={false},在它停止 滚动 行为的意义上“有效”,但它通过简单地关闭来实现这一点完全聚焦行为,这破坏了可访问性功能。正如我所说,如果你这样做,你首先会破坏使用 Reach Router 的主要原因之一。
那么,解决办法是什么?
基本上,HTMLElement.focus() 的这种副作用——滚动到焦点元素——似乎是不可避免的。因此,如果您想要无障碍功能,您必须采用滚动行为。
但话虽如此,可能有一种解决方法。如果您在每次路线更改时使用window.scrollTo(0, 0) 手动滚动到页面顶部,我相信这不会从可访问性角度“破坏”聚焦功能,但会从用户体验角度“修复”滚动行为。
当然,这有点笨拙且命令式的解决方法,但我认为这是在不破坏可访问性的情况下解决此问题的最佳(也许是唯一)解决方案。
这是我的实现方式
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) } />
</>
)