20

当我导航到新页面时,Reach Router 会向下滚动到页眉之后的页面内容(如果内容足够长)。我假设这是为了可访问性,但对我的应用程序来说不是必需的,而且它实际上很不和谐。可以禁用此行为吗?

请注意,我说的是 Reach Router 而不是 React Router。

到达路由器

4

7 回答 7

16

尝试使用<Router primary={false}>which 不会专注于路由组件。

https://reach.tech/router/api/Router

主要:布尔

默认为真。主路由器将管理对位置转换的关注。如果为 false,则不会管理焦点。这对于呈现为边栏、标题、面包屑等但不是主要内容的路由器很有用。

警告:如果您担心破坏可访问性,请参阅此答案:https ://stackoverflow.com/a/56996986/428780

于 2018-11-12T14:43:23.657 回答
11

这里的最佳答案虽然解决了 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) } />
  </>
)
于 2019-07-11T20:39:31.550 回答
4

基于@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
}
于 2019-08-21T19:15:07.447 回答
3

我必须使用多种方法来使其工作。即使进行了设置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
}

于 2019-05-11T19:27:36.693 回答
0

我用它制作了一个 npm 包以简化集成:https ://www.npmjs.com/package/reach-router-scroll-top

于 2020-10-22T15:15:48.777 回答
0

使用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 中是否有类似的解决方案,但该解决方案会更优化。

于 2020-01-10T17:35:14.860 回答
0

一定是有别的原因造成的。就像@Vinicius 说的那样。因为对于我的应用程序primary={false}确实有效。我有一个小应用程序和下面的路线。

<Router primary={false}>
  <Home path="/" />
  <Dashboard path="dashboard" />
  <Users path="users" />
  <Sales path="sales" />
  <Settings path="settings" />
</Router>
于 2019-04-11T05:38:05.417 回答