動的ルート

最終更新: 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].tsx
const 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スラッグにアクセスして必要なデータを取得する方法をお勧めします。

サーバーサイドからアクセする場合 getStaticPropsgetServerSideProps を使います。一般的にはDBアクセスをより節約できる getStaticProps を使いますが、この場合 getStaticPathes を合わせて定義する必要があります。

pages/[id].tsx
import { 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 を使って情報の更新を行なっています。