React Router v6.4で追加された<ScrollRestoration />が便利だった

2022/10/02に公開

React Router のv6.4で <ScrollRestoration /> というコンポーネントが追加されました。

https://reactrouter.com/en/main/components/scroll-restoration

SPAであるReactのアプリケーションにおいていい感じにスクロールの復元などを行ってくれるコンポーネントのようです。
ちょうど現在開発しているプロダクトで「URLが変わったときにスクロール位置をトップに戻したい」という要望が出たのですが、このコンポーネントで解決できそうだったのでメモを残したい思います。

<ScrollRestoration /> とは

MPAでは通常ブラウザがスクロール位置の復元などを行ってくれますが、SPAでは何もしない場合スクロール位置はそのままになります。

SPA MAP
リンクでのページ移動 そのまま TOPに移動
ブラウザバック/フォロー そのまま 復元

<ScrollRestoration /> コンポーネントを使うことでMPAのようなスクロール位置の管理をエミュレートできます。

また後述するgetKeypropを使うことでスクロールの振る舞いをある程度カスタマイズすることも可能です。

今までだと例えばlocationが変わったときにPageTopにスクロールさせたい場合などはuseEffect でlocationの変更を感知して、windows.scrollToでスクロールさせるなどの必要がありましたが <ScrollRestoration /> を使えばわざわざ自前でそれ用のコンポーネントを用意する必要がなくなります 🎉

使い方

使い方はとても簡単でRouteコンポーネント以下の任意の場所に置くだけです。
公式ではRootに置くことを推奨しています。

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

function RootRouteComponent() {
  return (
    <div>
      {/* ... */}
      <ScrollRestoration />
    </div>
  );
}

getKey

<ScrollRestoration />getKey というPropsを受けることができます。
このPropsでスクロール位置をどの単位で管理するかをカスタマイズすることができます。

<ScrollRestoration
  getKey={(location, matches) => {
    // default behavior
    return location.key;
  }}
/>

デフォルトでは location.key が指定されています。
この値はページ遷移するたびに変わるようで、同じURLであっても違う値が帰ってきます。

つまりユーザーが例えばリンクをたどってページを以下のように遷移したときに 1と3は同じURLであってもスクロール位置の復元位置は別になります。 (1でスクロールしたあと、3に移動したときにはまたページTOPに戻る)

  1. /home
  2. /search
  3. /home

ここで getKey の指定を次のように変更すると、1と3でスクロール位置が同じ位置になります。(3に移動したときにページTOPに戻らず、1のスクロール位置が復元される)

<ScrollRestoration
  getKey={(location, matches) => {
    return location.pathname;
  }}
/>

ここでは関数を記述できるので特定のパスの場合のみ location.pathnameを使い、それ以外では location.key を使うといったこともできます。

Preventing Scroll Reset

またページTOPにスクロールをさせたくないときには <Link /> 側からスクロール位置のリセットを抑制することもできます。

<Link preventScrollReset={true} />

デモ

公式のExampleがとてもわかりやすいのでそちらをご紹介します。

https://stackblitz.com/github/remix-run/react-router/tree/main/examples/scroll-restoration?file=src/App.tsx

まとめ

今回は <ScrollRestoration /> の紹介をしてみました。

SPAでは結構スクロール位置に悩まされることが多い気がします。
そんなときに React Routerを使っている場合はぜひこの <ScrollRestoration /> を使ってみてもらえたらと思います✨

React Router v6.4では他にも色々面白そうな変更(特にData Loaderまわり)があるので、きちんと見てみたいですね!

Discussion