別のページに移動するときに問題が発生しました。その位置は前のページのままです。そのため、自動的に一番上にスクロールしません。window.scrollTo(0, 0)
また、onChange
ルーターで使用しようとしました。私もscrollBehavior
この問題を修正するために使用しましたが、うまくいきませんでした。これについて何か提案はありますか?
32 に答える
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);
で、場所のパス名がいつ変更された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;
上記のコードから興味深いのは、移動するとき、/about
または/another-page
一番上にスクロールするときだけアクションが実行されるということです。ただし、/
スクロールの復元を行っている場合は発生しません。
コードベース全体はこちらにあります: https://github.com/rizedr/react-router-scroll-top
onUpdate={() => window.scrollTo(0, 0)}
メソッドが古くなっていることは注目に値します。
これは、react-router 4+ の簡単なソリューションです。
const history = createBrowserHistory()
history.listen(_ => {
window.scrollTo(0, 0)
})
<Router history={history}>
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;
私にとっては、すべての画面で機能window.scrollTo(0, 0)
しdocument.documentElement.scrollTo(0, 0)
ませんでした(1つの画面でのみ機能しました)。
次に、画面のオーバーフロー (スクロールが許可されている場所) が含まれていないことに気付きましたwindow
(いくつかの静的ポイントがあるためoverflow: auto
、他の div に入れました)。
これを実現するために、次のテストを行いました。
useEffect(() => {
const unlisten = history.listen(() => {
console.log(document.documentElement.scrollTop)
console.log(window.scrollTop)
window.scrollTo(0, 0);
});
return () => {
unlisten();
}
}, []);
すべてのログで、0 を取得しました。
そこで、スクロールが入っているコンテナを探して、ID を入力しました。
<div id="SOME-ID">
...
</div>
そして、ScrollToTop コンポーネントに次のように配置しました。
useEffect(() => {
const unlisten = history.listen(() => {
window.scrollTo(0, 0);
document.getElementById("SOME-ID")?.scrollTo(0, 0)
});
return () => {
unlisten();
}
}, []);
history.push("/SOME-ROUTE")
これで、画面で新しいルートに移動すると、一番上に移動します
スムーズスクロールオプション付き
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;
}
...
<Router>
<ScrollToTop />
...
</Router>
で、この関数をオブジェクトrouter.js
に追加するだけです。router
これは仕事をします。
scrollBehavior() {
document.getElementById('app').scrollIntoView();
},
このような、
**Routes.js**
import vue from 'blah!'
import Router from 'blah!'
let router = new Router({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior() {
document.getElementById('app').scrollIntoView();
},
routes: [
{ url: "Solar System" },
{ url: "Milky Way" },
{ url: "Galaxy" },
]
});
ここに別の方法があります。
react-router v4の場合、次の方法でリスナーをバインドして履歴イベントを変更することもできます。
let firstMount = true;
const App = (props) => {
if (typeof window != 'undefined') { //incase you have server-side rendering too
firstMount && props.history.listen((location, action) => {
setImmediate(() => window.scrollTo(0, 0)); // ive explained why i used setImmediate below
});
firstMount = false;
}
return (
<div>
<MyHeader/>
<Switch>
<Route path='/' exact={true} component={IndexPage} />
<Route path='/other' component={OtherPage} />
// ...
</Switch>
<MyFooter/>
</div>
);
}
//mounting app:
render((<BrowserRouter><Route component={App} /></BrowserRouter>), document.getElementById('root'));
リンクをクリックしてルートが変更された場合、スクロール レベルは 0 に設定されsetImmediate()
ますが、ユーザーがブラウザの [戻る] ボタンを押した場合、[戻る] ボタンが押されたときにブラウザが手動でスクロール レベルを前のレベルにリセットするため、機能しません。を使用setImmediate()
することで、ブラウザがスクロール レベルのリセットを完了した後に関数が実行され、目的の効果が得られます。
これはハッキーです(ただし機能します):追加するだけです
window.scrollTo(0,0);
レンダリングする();