导航到新页面后,停止到达路由器向下滚动页面

时间:2018-10-30 05:45:35

标签: reactjs reach-router

当我导航到新页面时,“到达路由器”向下滚动到页面内容的标题上方(如果内容足够长)。我假设这是为了可访问性,但对于我的应用程序不是必需的,并且实际上是很麻烦的。可以禁用此行为吗?

请注意,我所说的是“到达路由器而不是React Router”。

Reach Router

7 个答案:

答案 0 :(得分:10)

尝试使用<Router primary={false}>,它不会关注路线组件。

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

  

主要的:bool

     

默认为true。主路由器将专注于位置转换。如果为false,将不会管理焦点。这对于呈现为边,标头,面包屑等的路由器很有用,但对于主要内容却没有。

答案 1 :(得分:3)

在@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
}

答案 2 :(得分:1)

我不得不使用多种方法使它起作用。即使设置了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's scroll restoration guide

没有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
}

答案 3 :(得分:1)

在解决OP问题的同时,这里的最高答案可能不是大多数人想要的解决方案,因为它关闭了Reach路由器最重要的可访问性功能。

由于可访问性原因,到达路由器将匹配的<Route>的内容集中在路由更改为的事实上-因此,当出现以下情况时,屏幕阅读器等可以定向到最新更新的相关内容您导航到新页面。

它使用HTMLElement.focus()来执行此操作-see the MDN docs here

问题是默认情况下,此函数滚动到要聚焦的元素。 有一个preventScroll参数,可以用来关闭此行为,但是浏览器对此行为的支持不好,并且无论如何,Reach Router都不使用它。

primary上的Router道具用于关闭您可能拥有的所有嵌套 <Router>的行为-它不是不是旨在用于您的主要(主要)<Router>上-因此使用该名称。

将此false设置在主要<Router>上,如最佳答案所示,“有效”是指它停止了 scrolling 行为,但它可以通过以下方式实现此目的只需完全关闭聚焦行为,就会破坏可访问性功能。就像我说的那样,如果您这样做的话,您首先就要打破使用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) } />
  </>
)

答案 4 :(得分:0)

必须有另一件事导致此。就像@Vinicius所说。因为primary={false}对于我的应用程序确实有效。我有一个小应用程序,下面是我的路线。

<Router primary={false}>
  <Home path="/" />
  <Dashboard path="dashboard" />
  <Users path="users" />
  <Sales path="sales" />
  <Settings path="settings" />
</Router>

答案 5 :(得分: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中,您可以按照here的说明使用history.listen。我没有检查“到达路由器”中是否有类似的解决方案,但是该解决方案会更好。

答案 6 :(得分:0)

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