🦔

Next13以降のリダイレクト方法の整理

2024/03/05に公開
この記事では、Next13~ のapp routerをベースに記載しています。

Next.jsで選択できるリダイレクト方法を列挙して、
それらの特徴を見てみて、どんな使い方できるかをメモしてみました。
結構、自分用なメモにはなっていそうですので、そちらご了承ください。

Next.jsのリファレンスにも紹介があります。詳細はこちらから確認できます。
https://nextjs.org/docs/pages/building-your-application/routing/redirecting

CSR(client side rendering)の場合

useRouter

"use client"
import type { FC } from 'react'
import { useRouter } from 'next/navigation'
import { useRecoilState } from 'recoil'
import { userInfoAtom } from '@/shared/store/userInfoAtom'

export const MyPage: FC = () => {
  const router = useRouter()
  const [user] = useRecoilState(userInfoAtom)

  if (!user.isSignedIn) {
    router.push("/signIn")
  }

  return (
    <div>
      SignInしていないと見れないコンテンツです
    </div>
  )
}

https://nextjs.org/docs/pages/building-your-application/routing/redirecting#userouter-hook

  • useRouterでリダイレクトさせる。
  • Next云々ではなく、CSRのリダイレクト手法としては王道(?)
    • Reactに限らず、Vueのプロジェクトでも同様の手法が取られているのを見たことがあるので、クライアント側でのページ遷移方法としてはよくあるアプローチに思える。
  • propsglobal stateなどのstateによる処理条件を組み立てたいときに、いかようにも出来き、実装も比較的に簡単。
  • ページ遷移時に一瞬、コンポーネントがマウントされて画面のチラつきがどうしても発生してしまうので、ローディングスピナーなどの検討や実装が必要。
  • 再読み込み(リロード)でリフレッシュすれば全部解決なのに..系な処理については自力で副作用として実装する必要がある。

ちなみに、app routerでは 'next/router'からimportすると、Error: NextRouter was not mounted.になってしまいます。
nextのリファレンスに解決方法として 'next/navigation'から読み込んでと記載があります。
https://nextjs.org/docs/messages/next-router-not-mounted

import { useRouter } from 'next/router' // app routerではエラーになる。

SSR(server side rendering)の場合

redirect

  • 以前のgetServerSidePropsのような感じで、app routerの機能のserver actions / server componentsなどサーバー側でredirect関数を利用しリダイレクトさせる。
  • サーバー側でリダイレクトされるので、CSRで対応する場合のような画面のチラつきや、本来無くて良いDOMが残る問題は基本発生しない(はず)
  • フロントエンド側の状態管理を使えないので、そちらと同期しようと頑張ると管理が大変。
    • 認証系の処理でsession情報を取得するAPIが叩けたり、APIの通信結果の成功 / 失敗でリダイレクト処理を行える場合や、ページのUX重要視しない場合は、useRouterよりも個人としてはこちらを採用したいかも
import { redirect } from 'next/navigation'
import { cookies } from 'next/headers'
import { getSession } from '@/utils/getSession'

const HandleSignIn = async () => {
  'use server'

  const { isResult } = await getSession({ cookies })

  if (isResult) {
    redirect('...')
  }
}

https://nextjs.org/docs/pages/building-your-application/routing/redirecting#userouter

  • try~catch文の中で書こうとすると、Error: NEXT_REDIRECT at getRedirectErrorになるため、少し工夫が必要みたいです。
try {
 await getSession({ cookies })
 redirect('...') // Error: NEXT_REDIRECT at getRedirectError
} catch(error: unknown) {

middleware

  • next12から導入されている。
  • /src/ディレクトリがあり、かつapp routerの場合、/src/配下に置くことで起動。
  • matcherというフィルター内でマッチしたページへのリクエストへの到達前にリクエストをキャッチして前処理を実行させることができる。
  • session更新処理や、アプリケーション全体に関わる処理を実行させたいなど、仕様や要件により選択肢として使える。設計が結構難しそう。

https://nextjs.org/docs/pages/building-your-application/routing/middleware

import type { NextRequest } from 'next/server'

import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'
import { getSession } from '@/utils/getSession'

export async function middleware(req: NextRequest) {
  const res = NextResponse.next()
  const { origin } = req.nextUrl

  const { isResult } = await getSession({ cookies })

  if (isResult) {
    // サインイン済みの場合はトップページに遷移させる
    return NextResponse.redirect(`${origin}`)
  }

  return res
}

export const config = {
  matcher: ['/signIn'],
}

next.config

  • next10から導入されている。
  • 設定ファイルのnext.configでリダイレクトさせる。
  • ユースケースとしては、再構築などのプロジェクトで同一ドメイン内で、古いページURLから新しいページにリダイレクトさせたい場合などの一時的な用途に向いていそう。
/** @type {import('next').NextConfig} */
const nextConfig = {
  async redirects() {
    return [
      {
        source: '/old-blog/:path*',
        destination: '/blog/:path*',
        permanent: true,
      }
    ]
  },
};

export default nextConfig;

https://nextjs.org/docs/pages/building-your-application/routing/redirecting#redirects-in-nextconfigjs

まとめ

  • 改めて、nextでのリダイレクト方法について整理をしてみました。選択肢はたくさんあるので、うまく設計して活用したいものです。

Discussion