動的ルート
最終更新: 2023年03月14日
動的ルートとは
実装時点でURLの予測ができない場合は動的ルートを使います。
Next.js では各画面のURLに基づいて pages
にファイルを作成します。たとえば設定画面のURLを /settings
とする場合、 pages/settings.tsx
を作成しますが、たとえばTwitterのユーザープロフィール画面のURLはユーザーが決めるIDに基づいて決まるので、あらかじめユーザー画面のファイルを作ることができません。
あるユーザーが@parse
というアカウントを作った時にはじめてhttps://twitter.com/parse
にアクセスできるようになるので、 pages/parse.tsx
をあらかじめ作っておくことはできません。
このようにユーザーの操作など外的要因でURLが変わる場合に使うのが動的ルートです。
動的ルートの実装
ページファイルの作成
pages/
ディレクトリの中にブラケット([]
) で囲まれたファイル名でファイルを作成することで、その階層が動的ルートとして機能するようになります。
例pages/[id].tsx
たとえば上記のURLの場合、以下のどのURLにおいても上記のファイルが使われます。
/parsesato
/parseyamada
/parsetaro
優先度
Twitterの場合第一階層のパスが動的になっているので、ユーザーが存在する限り第一階層のパスに何を入れてもユーザープロフィール画面が表示されます。ただしプライバシーページについてはprivacy
というURLでユニークな画面が表示されます。
Next.js の動的ルートにおいては名指しで用意されたファイルは動的なページファイルよりも優先されます。前述の例で https://xxxxx/ユーザー名
という構造になっていますが、同時にpages/privacy.tsx
を作成した場合、pages/[id].tsx
よりもpages/privacy.tsx
が優先されることでprivacy
というURLでprivacy.tsx
がレンダリングされるようになります。
URLスラッグの使用
各ページはURLのIDに基づいてユーザー表示を切り替えたいので、URLの動的箇所(ブラケットの中身=URLスラッグ)を取得する必要があります。
クライアントサイドからアクセス
コンポーネントやページコンポーネントからURLスラッグにアクセスる場合はrouter.query.id
を使って取得できます。id
の部分はURLスラッグと一致していればなんでも構いません。たとえばpages/[userId].tsx
とする場合、 router.query.userId
で取得することになります。
以下の例ではURLスラッグを取得してユーザーを取得し、画面に表示しています。router.query
は最初のレンダリングでは空になるのと、その後別のIDの遷移する可能性があるためuseEffectの第二引数を使って変更を検知した上でユーザーを取得しています。
pages/[id].tsxconst UserPage = () => { const [user, setUser] = useState(); const router = useRouter(); useEffect(() => { // idが変わったら(取得できたら)、ユーザーをDBから取得する getUser(router.query.id) .then(result => setUser(result)); }, [router.query.id]); if (user) { return <div>{user.name}</div> } else { return null; } }
クライアントからURLスラッグにアクセスしてデータを取得するアプローチの場合ページにアクセスするユーザーごとにリクエストが発生するため、特に理由がなければ後述するサーバーサイドからのアプローチをお勧めします。
サーバーサイドからアクセスする
VercelなどにホストしてISRを有効にできる場合、サーバーサイドからURLスラッグにアクセスして必要なデータを取得する方法をお勧めします。
サーバーサイドからアクセする場合 getStaticProps や getServerSideProps を使います。一般的にはDBアクセスをより節約できる getStaticProps
を使いますが、この場合 getStaticPathes を合わせて定義する必要があります。
pages/[id].tsximport { GetStaticProps, GetStaticPaths } from 'next' const UserPage = ({ user }) => { if (user) { return <div>{user.name}</div> } else { return null; } } export const getStaticPaths: GetStaticPaths = async (context) => { return { paths: [], fallback: 'blocking' }; } export const getStaticProps: GetStaticProps = async (context) => { // URLスラッグを使ってユーザーを取得 const user = await getUser(context.params.id); // getUserはダミーメソッド return { props: { user }, revalidate: 10, // 前回の生成から10秒以上経過していたら再生成する } }
revalidate を使って情報の更新を行なっています。