React Routerの公式に載ってるやつを、ReduxとConnected React Router使ってるアプリでやっただけ。

[ react@16.13.0 / react-router-dom@5.1.2 / connected-react-router@6.8.0 ]

React Router公式のサンプル通り、以下のコンポーネントを作ります。

ScrollToTop.jsx
import React from 'react';
import { withRouter } from 'react-router-dom';

class ScrollToTop extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0);
    }
  }
  
  render() {
    return null;
  }
}

export default withRouter(ScrollToTop);

このコンポーネントを、index.jsxReactDOM.render() してるところに以下のように設置します。

index.jsx (抜粋)
ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <ScrollToTop />
      <App />
    </ConnectedRouter>
  </Provider>,
  document.getElementById('root')
);

これで、リンクをクリックまたは history.push() して画面遷移した時に、スクロール位置が先頭に戻るようになります。

この方法の場合、ブラウザで戻る/進むを行った時は、スクロール位置は維持されます。

画面全体が更新されるたびに <ScrollToTop /> が一緒に更新されるから、こういうことができるんだと思います。


なお、公式のページで最初に書かれてる以下のものは、ブラウザで戻る/進むを行った時もスクロール位置が先頭に戻ります。

ScrollToTop.jsx
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;
}

Reactのバージョンが16.8以上の場合はこれを使えるとのことですが、挙動が違うのでは良し悪しな気がします。