メインコンテンツにスキップ

App Routerを本番投入した感想

前書き

最近本業で App Router をプロダクション環境にリリースしたので感想や設計について書いていく。 App Routerがそもそもどのようなものかはこちら

感想

感想としては、コロケーションすることでページ単位のコンポーネントをまとめて置くことができるのでシンプルに作ることができる。 Moving Client Components to the Leaves にもあるが、CCをできるだけツリーの端に置くことは重要だった。 あとはApp Routerに対応していないライブラリを使用する場合は自分でexportし直す必要があるが、これもドキュメントに書かれていた ⇨ Third-party packages

設計

App Routerでのフォルダ分割をどうするかについては、コロケーションを重視してフォルダを切った。わかりやすくて良い。 具体的には下記のようにした。

.
└── app/
    ├── great_page/
   ├── _components/
   ├── Form.tsx
   ├── Form.test.tsx
   ├── Form.stories.tsx
   └── ...
   ├── _api/
   ├── getUser.ts
   └── ....
   ├── __types/
   ├── user.ts
   └── ...
   ├── page.tsx
   ├── error.tsx
   └── layout.tex
    └── _components/
        ├── Button.tsx
        └── ....

_をフォルダ名の先頭に付けると、ルートとして認識されないのを利用して、ページで使うコンポーネントはページ名のフォルダ内部に集約した。 一方で全体のページで使用されるコンポーネントはapp直下に_componentsというフォルダを作りそこに置いた。

後から知ったがNext.jsの中の人もおすすめしていた設計だった。

Great feedback, thank you! It is indeed up to you, the pattern we found useful so far is a _components directory collocated with the route if there are specific components. Otherwise use app/_components. https://twitter.com/timneutkens/status/1659313703374712833

それぞれのルート直下のpage.tsxでコンポーネントを呼び出して、非同期コンポーネントはSuspenceでラップしてfallbackを表示させるという風にした。

データフェッチに関しては、Hooksなどは経由せずにコンポーネント内部でそのままコンポーネントが必要なデータを取得するようにし、コロケーションをここでも実行した。 と言っても公式ドキュメントにもある通り。

import Albums from './albums'

async function getArtist(username: string) {
  const res = await fetch(`https://api.example.com/artist/${username}`)
  return res.json()
}

async function getArtistAlbums(username: string) {
  const res = await fetch(`https://api.example.com/artist/${username}/albums`)
  return res.json()
}

export default async function Page({ params: { username } }: { params: { username: string } }) {
  // Initiate both requests in parallel
  const artistData = getArtist(username)
  const albumsData = getArtistAlbums(username)

  // Wait for the promises to resolve
  const [artist, albums] = await Promise.all([artistData, albumsData])

  return (
    <>
      <h1>{artist.name}</h1>
      <Albums list={albums}></Albums>
    </>
  )
}
// https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#parallel-data-fetching

余談

初めてNext.jsを触る人とペアプロした際に、わかりやすいですねと言っていて、自分からするとPage Routesからだいぶ複雑になったと思っていたが メンタルモデルは意外と単純になったのかもしれないと思った。

4 min read
Next.js