Next.jsにおけるSPA的遷移の考え方
Next.jsにおけるSPA的遷移の考え方
追記
(2021年09月21日): Linkタグを用いるとSSR時でもブラウザリロードすることなくコンポーネントを再レンダーすることができる。
具体的には、サーバに対してGETリクエストを送り、getServerSidePropsを実行させた後をpropsを受け取り、再レンダーするような挙動になっている。
これとNext.jsのLayoutシステムを用いれば、ヘッダをアンマウントさせずに画面遷移はできるが、ページ内部のモーダルなどはレイアウトにするのは厳しいので本記事で説明するshallow
オプションを扱うのが良いと思われる。
背景
インターン先で既存SSR(React)プロジェクトをNext.jsに移行する際に、プロフィールページのタブやモーダルといった箇所でコンポーネントの切り替えと共にURLも変わるようなSPA的遷移の実装を移行するのに苦労した
具体的にはreact-routerのhistory.push()
にあたる機能が Next.js ですぐに見つけられなかった
今まではタブなどの切り替え時にhistory.push()
をすることで実装していたが、
それがNext.jsではどのようにすれば実装できるかについて議論する
単語定義
SPA的遷移
ここではコンポーネントの切り替えに応じてHistoryが変わることとする
- e.g. react-routerでは
<Switch>
,<Route>
などでコンポーネントの切り替えを行い、そこにHistoryの変更が伴うような実装がされている
ページ
ここでは単一のサーバ側でのエンドポイントのこととする
- react-routerで生成されるパス(例えば
/
と/about
)は同じ1つのページを指す - Single Page Application という略称を考えるととても正しいはず
/pages
の概念
Next.jsにおけるNext.jsでは/pages
に存在しているファイルたちはコンポーネントを出し分けるために存在するわけではなく、一つ一つが単一のエンドポイントとして存在する(ファイル同士が関連することはない)
それに対してreact-routerは<Switch>
, <Route>
などでHistoryオブジェクトに同期させてコンポーネントを出し分けてる
なぜ今までhistory.push
でタブやモーダルがいい感じに動いていたかというとページが一つだったためである
もう少し具体的にいうとexpress側で設定していたエンドポイントが1つだったため
app.get("/*", async (req, res, _next) => {
...
}
例えば下記のようなエンドポイントが用意されていた時
app.get("/id/:userSlug", async (req, res, _next) => {})
app.get("/id/:userSlug/connections", async (req, res, _next) => {})
/id/:userSlug
から/:id/:userSlug/connections
に対してサーバ側の処理もせず、ページリロードもせず、Historyオブジェクト内の履歴スタックのみ追加することは出来ないはずである
(/id/:userSlug
内のreact-routerと/id/:userSlug/connections
内のreact-routerはお互いを認識しない)
SPA的遷移の実現方法
一般的なSPA的遷移の実装方法
上述の/pages
の話だが、果たしてreact-routerを用いてコンポーネントを切り替えるときページが変わっているかというと変わっていない
(ユーザ的にではなくアプリケーション的に)
これらのことからSPA的遷移を実装する際には
- 同一ページ内で
- コンポーネントの切り替えを行う実装を用意し
- コンポーネントの切り替え時にHistoryオブジェクトを変更してあげる
仕組みを用意すれば良いことがわかった
Next.jsでのSPA的遷移の実装方法
- 同一ページ内で
- コンポーネントの切り替えを行う実装を用意し
- コンポーネントの切り替え時にHistoryオブジェクトを変更する
これを一つ一つ行えば良い。
同一ページ
-
[...slugs].tsx
- Next.jsでは
/id/[...slugs].tsx
とすると/id/XX
も/id/XX/connections
も同じページとして扱うことができる。 - Dynamic Routes
- Next.jsでは
コンポーネントの切り替えを行う実装
- これはタブなりモーダルなりでそれぞれの実装を行う
- e.g. react-tabs, イベント
コンポーネントの切り替え時にHistoryオブジェクトを変更する
-
shallow
オプション-
Link
タグ、NextRouter.push()
で指定することができる - Shallow Routing
-
shallow
オプションは結局何をやっているのか
shallowオプションが何をやっているかというと
getServerSideProps
などをせず、表示をリロードさせず、Hisotryオブジェクトのみを変える動作
つまり上述の「Historyオブジェクトを変更する」部分である。
shallow
オプションが同一ページ内でしか動作しない理由
- SPA的遷移の実装方法を考えるとそもそもSPA的遷移は同一ページ内でしか行われないことが分かった
- このことから同一ページ内でしか
shallow
オプションが動作しないのは制限というよりもそもそもの前提によるものであることが分かる
結論
ここまで「Next.jsにおけるSPA的遷移の考え方」について議論してきたが、
Next.jsのページ機構とSPA的遷移の実装方法を考えるとNext.jsでのSPA的遷移は実装可能なことが分かった。
shallow
オプションを実装過程で使用することになるが、このオプションについて不便に感じるかはそれぞれの感じ方であり、
SPA的遷移に必要な機能は持っていることが分かった。
Discussion