react-router在每次转换时滚动到顶部

adr*_*nto 64 javascript reactjs react-router react-router-redux

导航到另一个页面时出现问题,其位置将保持与之前的页面相同.所以它不会自动滚动到顶部.我也试过window.scrollTo(0, 0)在onChange路由器上使用.我也使用scrollBehavior来解决这个问题,但它没有用.对此有何建议?

mtl*_*mtl 81

React Router v4的文档包含用于滚动恢复的代码示例.这是他们的第一个代码示例,当页面导航到以下时,它作为"滚动到顶部"的站点范围解决方案:

class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    return this.props.children
  }
}

export default withRouter(ScrollToTop)
Run Code Online (Sandbox Code Playgroud)

然后将其呈现在应用程序的顶部,但在路由器下方:

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)

// or just render it bare anywhere you want, but just one :)
<ScrollToTop/>
Run Code Online (Sandbox Code Playgroud)

^直接从文档中复制

显然这适用于大多数情况,但更多关于如何处理选项卡式接口以及为什么没有实现通用解决方案.

  • 摘录自文档“因为浏览器开始处理“默认情况”并且应用程序有不同的滚动需求(如本网站!),所以我们不提供默认滚动管理。本指南应该帮助您实现您所拥有的任何滚动需求。`让我感到难过,因为他们继续提供代码示例的所有选项实际上都可以通过属性设置烘焙到控件中,并围绕默认行为进行一些约定,然后可以被改变。恕我直言,这感觉超级黑客和未完成。意味着只有高级反应开发人员才能正确进行路由,并且没有#pitOfSuccess (3认同)
  • 您还应该在滚动到顶部的同时重置键盘焦点。我写了一个东西来照顾它:https://github.com/oaf-project/oaf-react-router (2认同)
  • oaf-react-router 似乎解决了这里所有“解决方法”都忽略的重要可访问性问题。+100 @danielnixon (2认同)

Sae*_*ani 52

反应 16.8+

如果您正在运行 React 16.8+,这很容易使用一个组件来处理,该组件将在每次导航时向上滚动窗口:
这是在 scrollToTop.js 组件中

import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
}
Run Code Online (Sandbox Code Playgroud)

然后在你的应用程序顶部渲染它,但在路由器
下面是在 app.js 中

import ScrollToTop from "./scrollToTop";

function App() {
  return (
    <Router>
      <ScrollToTop />
      <App />
    </Router>
  );
}
Run Code Online (Sandbox Code Playgroud)

或者在 index.js 中

import ScrollToTop from "./scrollToTop";

ReactDOM.render(
    <BrowserRouter>
        <ScrollToTop />
        <App />
    </BrowserRouter>
    document.getElementById("root")
);
Run Code Online (Sandbox Code Playgroud)

  • 仅适用于react-router-dom v6 的答案。谢谢 (11认同)
  • 是的,这是一个合适的解决方案,可以在使用钩子的 React 应用程序中很好地工作。 (3认同)
  • 谢谢,这也适用于“react-router-dom”:“^6.2.1”和“react”:“^17.0.2”。 (2认同)

zur*_*fyx 32

但上课是如此2018

带有React Hooks的ScrollToTop实现

ScrollToTop.js

import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return (null);
}

export default withRouter(ScrollToTop);
Run Code Online (Sandbox Code Playgroud)

用法:

<Router>
  <Fragment>
    <ScrollToTop />
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </Fragment>
</Router>
Run Code Online (Sandbox Code Playgroud)

ScrollToTop也可以实现为包装器组件:

ScrollToTop.js

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return <Fragment>{children}</Fragment>;
}

export default withRouter(ScrollToTop);
Run Code Online (Sandbox Code Playgroud)

用法:

<Router>
  <ScrollToTop>
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>
Run Code Online (Sandbox Code Playgroud)

  • 迄今为止我在互联网上看到的最佳解决方案。我只是有点困惑为什么react-router项目没有将属性“scrollToTop”添加到“&lt;Route&gt;”。这似乎是非常常用的功能。 (10认同)
  • 它有效,但不处理返回行为。我的意思是当返回到上一页时,上一页也会从滚动顶部加载。例如,我有 100 个项目的列表,单击特定项目会导航到另一个页面。现在,当我单击在第一页上向下滚动的列表项并转到另一个页面时,另一个页面将从滚动到顶部加载,但如果我返回,那么第一页也会从滚动到顶部加载。然而,它应该从我们停止的地方加载,我的意思是从滚动的底部开始。 (10认同)
  • 另外,你可以使用 useHistory hook 而不是 withRouter HOC (6认同)
  • 你应该检查`action !== 'POP'`。侦听器接受操作作为第二个参数 (3认同)
  • 对于 TypeScript 用户, `history` 属性的正确类型是 `History` from `import { History } from "history";` (3认同)
  • 我需要将“history”添加到 useEffect 的依赖项列表中 (2认同)

Gab*_*e M 32

您可以添加到 Route 组件的 React Hook。使用useLayoutEffect而不是自定义侦听器。

import React, { useLayoutEffect } from 'react';
import { Switch, Route, useLocation } from 'react-router-dom';

export default function Routes() {
  const location = useLocation();
  // Scroll to top if path changes
  useLayoutEffect(() => {
    window.scrollTo(0, 0);
  }, [location.pathname]);

  return (
      <Switch>
        <Route exact path="/">

        </Route>
      </Switch>
  );
}
Run Code Online (Sandbox Code Playgroud)

更新:更新为使用useLayoutEffect而不是useEffect,以减少视觉卡顿。这大致翻译为:

  • useEffect: 渲染组件 -> 绘制到屏幕 -> 滚动到顶部(运行效果)
  • useLayoutEffect: 渲染组件 -> 滚动到顶部(运行效果) -> 绘制到屏幕

根据您是否正在加载数据(想想微调器)或者您是否有页面过渡动画,useEffect可能更适合您。

  • 奇怪,但抛出错误“无法读取未定义的属性(读取'位置')” (3认同)

Luk*_*kas 28

这个答案是遗留代码,路由器v4 +检查其他答案

<Router onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}>
  ...
</Router>
Run Code Online (Sandbox Code Playgroud)

如果它不起作用,你应该找到原因.在里面componentDidMount

document.body.scrollTop = 0;
// or
window.scrollTo(0,0);
Run Code Online (Sandbox Code Playgroud)

你可以使用:

componentDidUpdate() {
  window.scrollTo(0,0);
}
Run Code Online (Sandbox Code Playgroud)

你可以添加一些标志,如"scrolled = false",然后在更新中:

componentDidUpdate() {
  if(this.scrolled === false){
    window.scrollTo(0,0);
    scrolled = true;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 在反应路由器v4中不推荐使用`onUpdate` hook - 只想指出它 (3认同)
  • @MattVoda在下面写了一个关于如何使用react-router-v4实现这一目标的答案 (3认同)

Dra*_*scu 25

对于react-router v4,这是一个实现滚动恢复的create-react-app:http://router-scroll-top.surge.sh/.

为此,您可以创建装饰Route组件并利用生命周期方法:

import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';

class ScrollToTopRoute extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.path === this.props.location.pathname && this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    const { component: Component, ...rest } = this.props;

    return <Route {...rest} render={props => (<Component {...props} />)} />;
  }
}

export default withRouter(ScrollToTopRoute);
Run Code Online (Sandbox Code Playgroud)

componentDidUpdate我们可以检查位置路径名何时更改并将其与path道具匹配,如果满足,则恢复窗口滚动.

这种方法有什么好处,我们可以拥有恢复滚动的路由和不恢复滚动的路由.

以下是App.js如何使用上述内容的示例:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Lorem from 'react-lorem-component';
import ScrollToTopRoute from './ScrollToTopRoute';
import './App.css';

const Home = () => (
  <div className="App-page">
    <h2>Home</h2>
    <Lorem count={12} seed={12} />
  </div>
);

const About = () => (
  <div className="App-page">
    <h2>About</h2>
    <Lorem count={30} seed={4} />
  </div>
);

const AnotherPage = () => (
  <div className="App-page">
    <h2>This is just Another Page</h2>
    <Lorem count={12} seed={45} />
  </div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <ul className="App-nav">
              <li><Link to="/">Home</Link></li>
              <li><Link to="/about">About</Link></li>
              <li><Link to="/another-page">Another Page</Link></li>
            </ul>
          </div>
          <Route exact path="/" component={Home} />
          <ScrollToTopRoute path="/about" component={About} />
          <ScrollToTopRoute path="/another-page" component={AnotherPage} />
        </div>
      </Router>
    );
  }
}

export default App;
Run Code Online (Sandbox Code Playgroud)

从上面的代码中,有趣的是,只有在导航到/about/another-page滚动到顶部动作时才会执行.但是,当进行时,/不会发生滚动恢复.

整个代码库可以在这里找到:https://github.com/rizedr/react-router-scroll-top

  • 正是我一直在寻找的东西!我必须在 ScrollToTopRoute 的 componentDidMount 中添加scrollTo,因为我将我的路线包装在 Switch 组件中(我还删除了 if 因为我希望它在每个安装上触发) (2认同)

Has*_*zad 16

对于“REACT-ROUTER-DOM v6 及更高版本”

我通过创建一个包装函数并将其包装在所有路由周围解决了以下问题。

请按照以下步骤操作:

1:您需要导入以下内容:

import {Routes, Route, BrowserRouter as Router, useLocation} from 'react-router-dom';
import {useLayoutEffect} from 'react';
Run Code Online (Sandbox Code Playgroud)

2:在“App”函数上方编写一个包装函数:

const Wrapper = ({children}) => {
  const location = useLocation();
  useLayoutEffect(() => {
    document.documentElement.scrollTo(0, 0);
  }, [location.pathname]);
  return children
} 
Run Code Online (Sandbox Code Playgroud)

3:现在将您的路由包装在包装函数中:

<BrowserRouter>
  <Wrapper>
    <Navbar />
      <Routes>
        <Route exact path="/" element={<Home/>} />
        <Route path="/Products" element={<Products/>} />
        <Route path="/Login" element={<Login/>} />
        <Route path="/Aggressive" element={<Aggressive/>} />
        <Route path="/Attendance" element={<Attendance/>} />
        <Route path="/Choking" element={<Choking/>} />
        <Route path="/EmptyCounter" element={<EmptyCounter/>} />
        <Route path="/FaceMask" element={<FaceMask/>} />
        <Route path="/Fainting" element={<Fainting/>} />
        <Route path="/Smoking" element={<Smoking/>} />
        <Route path="/SocialDistancing" element={<SocialDistancing/>} />
        <Route path="/Weapon" element={<Weapon/>} />
      </Routes>
    <Footer />
  </Wrapper>
</BrowserRouter>
Run Code Online (Sandbox Code Playgroud)

这应该可以解决问题。


leh*_*ang 15

值得注意的是该onUpdate={() => window.scrollTo(0, 0)}方法已经过时了。

这是适用于react-router 4+的简单解决方案。

const history = createBrowserHistory()

history.listen(_ => {
    window.scrollTo(0, 0)  
})

<Router history={history}>
Run Code Online (Sandbox Code Playgroud)

  • 当您在 url 末尾添加“#”和“?”时,您会遇到问题。在第一种情况下,您应该滚动到顶部,在第二种情况下,您根本不应该滚动 (4认同)
  • 不幸的是,这不适用于 BrowserRouter。 (4认同)

Kep*_*pro 11

React 钩子 2020 :)

import React, { useLayoutEffect } from 'react';
import { useLocation } from 'react-router-dom';

const ScrollToTop: React.FC = () => {
  const { pathname } = useLocation();
  useLayoutEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
};

export default ScrollToTop;
Run Code Online (Sandbox Code Playgroud)

  • 打字稿定义 (2认同)

Tho*_*tre 9

在下面的组件中 <Router>

只需添加一个 React Hook(以防您不使用 React 类)

  React.useEffect(() => {
    window.scrollTo(0, 0);
  }, [props.location]);
Run Code Online (Sandbox Code Playgroud)


小智 9

2021 年 8 月

您可以在 App.js 中执行此操作,而不是在每个页面中执行此操作

import { useLocation } from "react-router-dom";

const location = useLocation();
useEffect(() => {
  window.scrollTo(0,0);
}, [location]);
Run Code Online (Sandbox Code Playgroud)

设置位置useEffect将确保每次路径更改时滚动到顶部。


Jha*_*noy 7

我的应用程序遇到了同样的问题.使用下面的代码片段帮助我在点击下一个按钮时滚动到页面顶部.

<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
...
</Router>
Run Code Online (Sandbox Code Playgroud)

但是,问题仍然存在于浏览器上.经过大量的试验,意识到这是因为浏览器窗口的历史对象,它有一个属性scrollRestoration,设置为auto.Setting这个手动解决了我的问题.

function scrollToTop() {
    window.scrollTo(0, 0)
    if ('scrollRestoration' in history) {
        history.scrollRestoration = 'manual';
    }
}

<Router onUpdate= {scrollToTop} history={browserHistory}>
....
</Router>
Run Code Online (Sandbox Code Playgroud)


Kri*_*ver 7

钩子是可组合的,从 React Router v5.1 开始,我们有了一个useHistory()钩子。因此,基于@zurfyx 的回答,我为此功能创建了一个可重复使用的钩子:

// useScrollTop.ts
import { useHistory } from 'react-router-dom';
import { useEffect } from 'react';

/*
 * Registers a history listener on mount which
 * scrolls to the top of the page on route change
 */
export const useScrollTop = () => {
    const history = useHistory();
    useEffect(() => {
        const unlisten = history.listen(() => {
            window.scrollTo(0, 0);
        });
        return unlisten;
    }, [history]);
};
Run Code Online (Sandbox Code Playgroud)


Ric*_*yce 7

2021 (React 16) - 基于@Atombit 的评论

下面滚动到顶部,但也保留历史滚动位置。

function ScrollToTop() {
  const history = useHistory()
  useEffect(() => {
    const unlisten = history.listen((location, action) => {
      if (action !== 'POP') {
        window.scrollTo(0, 0)
      }
    })
    return () => unlisten()
  }, [])
  return (null)
}
Run Code Online (Sandbox Code Playgroud)

用法:

<Router>
  <ScrollToTop />
  <Switch>
    <Route path="/" exact component={Home} />
  </Switch>
</Router>
Run Code Online (Sandbox Code Playgroud)


Ser*_*giu 6

我的解决方案:我在我的屏幕组件中使用的一个组件(我想要滚动到顶部)。

import { useLayoutEffect } from 'react';

const ScrollToTop = () => {
    useLayoutEffect(() => {
        window.scrollTo(0, 0);
    }, []);

    return null;
};

export default ScrollToTop;
Run Code Online (Sandbox Code Playgroud)

这会在返回时保留滚动位置。使用 useEffect() 对我来说是有问题的,当返回时,文档会滚动到顶部,并且在已经滚动的文档中更改路由时也会有闪烁的效果。


小智 6

这是我的方法,基于其他人在以前的帖子中所做的。想知道这是否是 2020 年使用位置作为防止重新渲染的依赖项的好方法?

import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function ScrollToTop( { children } ) {
    let location = useLocation();

    useEffect( () => {
        window.scrollTo(0, 0);
    }, [ location ] );

    return children
}
Run Code Online (Sandbox Code Playgroud)


ata*_*min 6

具有平滑滚动选项

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

export default function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo({
      top: 0,
      left: 0,
      behavior: 'smooth', 
    });
  }, [pathname]);

  return null;
}
Run Code Online (Sandbox Code Playgroud)

...

<Router>
      <ScrollToTop />
     ...
    </Router>
Run Code Online (Sandbox Code Playgroud)


Lui*_*bro 5

我想为那些正在使用的人分享我的解决方案,react-router-dom v5因为这些 v4 解决方案都没有为我工作。

解决我的问题的是安装react-router-scroll-top并将包装器放在<App />这样的位置:

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)
Run Code Online (Sandbox Code Playgroud)

就是这样!有效!


adr*_*nto -1

我发现这ReactDOM.findDomNode(this).scrollIntoView()是有效的。我把它放在里面了componentDidMount()