2021-6-29

React系で戻るボタンでスクロール位置復元は割と面倒だなって思った話

雑な技術メモ

結論

Reactで意識せずに戻るボタン押した際にスクロール位置復元をしたいなら、Next.js + getStaticProps、つまりSSGやISRにするしかないと思った。

getServerSidePropsやgetInitialPropsでやる場合はexperimentalなオプションを有効にしないといけないので正常動作が保証されない。useSWRでやる場合はスクロール位置復元したい画面全てにuseSWR使わないといけないのでコストがかかるので。

以下うだうだ書いてるだけ

戻るボタンを押した際に前のスクロール位置が復元されているのは、そうであって当然の物だと思っている。一覧系画面と詳細系画面を行き来することなんてWEBサイトを見ていると頻繁にあるからだ。

で、VueやReactでSPAを作っていると復元されないことが頻繁にある。よくある原因としてページ表示後にデータを取得するような処理をしていることが挙げられる。

  1. 戻るボタンを押して一覧画面に戻ろうとする
  2. 前のスクロール位置に戻ろうとする
  3. データが取得されておらず一覧が表示されていないので、スクロールできない
  4. スクロール位置復帰処理後にデータ取得して表示しても後の祭り

のような感じでスクロール位置が復元されない。

解決方法としては、

  • データを全てRedux等ストアに保存するようにし、戻った際に最初からデータが存在するようにする
    • データ取得処理に関してuseSWRを使うとかもあり

というのが挙げられる。個人的にはスクロール位置復元のためとしては大仰な気がしてしょうがない。スクロール位置復元という基本機能は意識せずとも勝手に実現されるような形であってほしい。

Nuxt/Nextの場合だと、ページ遷移前に初期表示に必要なデータを取得することで、フレームワークとしてスクロール位置復元をサポートしている。

Nextの場合はちょっとややこしくて、getStaticPropsを使っている場合は何もしなくてもスクロール位置復元が実現するが、それ以外(getServerSidePropsやgetInitialProps)の場合は、next.config.jsに以下の設定を追加しないといけない。

module.exports = {
  experimental: {
    scrollRestoration: true,
  },
}

このオプションを有効にすると history.scrollRestoration がmanualになる。これがautoだとブラウザの機能でpopstateのタイミングで保持したスクロール位置に移動してくれるのだが、getServerSidePropsやgetInitialPropsの場合popstateのタイミングとデータ取得完了するタイミングにズレがあるので正常に動作してくれない。それを解決するためにmanualにしてNext側で制御するようにしているのだと思われる。(ちなみにこれに関してはNext.jsのマニュアルに記載がなかったので色々と心配なオプションではある)

あとNextはSPAモードが無いので、APIサーバー以外に追加でNode.jsサーバーが一つ必要になってくる。(NuxtだとSPAモードがあるのでそこらへんはNuxtの方がいいなぁという気持ち)

色々考えた結果「React系でスクロール位置復元面倒だぁ」といった感想を持った。

React系でスクロール位置復元をしたいのであれば、Next.js + getStaticPropsの画面のみ復元されるという認識でいたほうが良さそう。それ以外はコストや安定性で色々と大変だと思う。

参考記事

https://mmazzarolo.com/blog/2021-04-10-nextjs-scroll-restoration/