【Next.js v13】useRouterとLinkコンポーネントについて

最近React.js(Next.js)を学習し始めた学生エンジニアです。

メインはバックエンドをやっているのですが、インターンとして参加している会社でどっちもやってもらうと言われましたので現在キャッチアップ中です。
今回はNext.jsが提供している二つの画面遷移方法についてその挙動や使用法の違いを復習がてらまとめました。なおversionは13.4を使用しています。

## Next.jsにおける画面遷移

Next.jsには画面遷移のパフォーマンス向上のため、いくつかの内部的な仕組みがあります。それを踏まえた上でLinkコンポーネントやuseRouterを使用するのが良いかと思いましたのでそれを先に紹介したいと思います。
今回はいくつかある特徴のうち、最も重要な二つについて紹介します。

### 1️⃣ Prefetching

prefetchとはその表現にも表されている通りpre(事前に)fetch(取得)⇒遷移先ページを事前に取得しておくことで、遷移時にページのロードが起こらないようにする機能のことをいいます。
本日紹介するLinkとuseRouterそれぞれに対してPrefetchingを行うようにする方法があります。

**Linkコンポーネント**
viewportにLinkが表示された際に、デフォルトでは自動的に遷移先ページのprefetchが実行されます。prefetchはページの初回表示時かスクロールによって表示された際に実行されます。

**useRouter**
useRouterではrouter.prefetch(url)とすることでprefetchが可能ですが、特に設定がない場合は実行されません。ですので、初回ページ表示時にuseRouterの遷移先をuseEffectでprefetchすると良いのかなと思います。

また、Linkコンポーネントはページの遷移先がstatic page(普通のページ (/page))かdynamic page(詳細ページなど (/page/{id}))でprefetchの挙動が異なります。

**static pageの場合**
デフォルトで全てのページが自動的にprefetchされます。

**dynamic pageの場合**
最初のloading.jsファイルまでの共有レイアウトのみがプリフェッチされ、30秒間キャッシュされます。これによって、全てのdynamic pageをprefetchするコストを避けつつ、高速なページ表示を実現することができています。
⇒ dynamic pageはたくさんあるのでそれぞれに対してのprefetchは行わないということですね。

### 2️⃣ Caching

Next.jsにはRouter Cacheと呼ばれるクライアント側のキャッシュがあり、prefetchされた部分と、過去訪問したページに関してはそのキャッシュに保存されています。
そのため再訪問する際にサーバーにリクエストをすることなく、ページの表示が可能となり、ますますパフォーマンス向上につながっていきます。

## Linkコンポーネント

aタグを拡張したNext.js独自のリンク作成のタグになります。
Next.jsの大概のページ遷移はこれで行います。

### 使用法

**基本**
以下のようにLinkコンポーネントをnext/linkからインポートします。
Linkコンポーネントはhrefというpropsを持ち、そこに遷移先のページを指定してあげることでリンクとして機能します。

```jsx
import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}
```

**詳細ページへのリンク**
もし、詳細ページ等へのリンクを作りたい場合にはテンプレートリテラルを使用して実現することができます。

```jsx
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
```

**リンクがアクティブか判定する**
リンクがアクティブな際に、スタイリングを変えたいなどと言ったことがあるかと思います。
そのような時はusePathname()を使用することによって実現することができます。

```jsx
'use client'
 
import { usePathname } from 'next/navigation'
import Link from 'next/link'
 
export function Navigation({ navLinks }) {
  const pathname = usePathname()

  return (
    <>
      {navLinks.map((link) => {
　　　　　// 今のpathとlinkのhrefを比較して同じならtrueを返す
        const isActive = **pathname === link.href**

        return (
          <Link
            className={isActive ? 'text-blue' : 'text-black'}
            href={link.href}
            key={link.name}
          >
            {link.name}
          </Link>
        )
      })}
    </>
  )
}
```

### Props

**href**
文字列でURLを指定します。
オブジェクトを入れて指定することもできます。

```jsx
<Link href="/dashboard">Dashboard</Link>

// /about?name=test
<Link
  href={{
    pathname: '/about',
    query: { name: 'test' },
  }}
>
  About
</Link>
```

**replace**
デフォルトの設定はfalseになっています。
 true の場合、ブラウザーの履歴に新しい URL を追加するのではなく、現在の履歴状態を置き換えます。

```jsx
<Link href="/dashboard" replace>
	Dashboard
</Link>
```

**scroll**
デフォルトの設定はtrueになっています。
trueの場合はページ遷移時にページのトップが表示されます。Linkを押した時点でのスクロール位置が継承されます。

```jsx
<Link href="/dashboard" scroll={false}>
	Dashboard
</Link>
```

**prefetch**
デフォルトの設定はtrueになっています。
prefetchに関しては先ほどのセクションで説明しましたので割愛します。

```jsx
<Link href="/dashboard" prefetch={false}>
	Dashboard
</Link>
```

## useRouter

### 使用法

**基本**
useRouterはclient component内でのみ使用することが可能です。
以下のようにnext/navigationよりインポートし、pushメソッド等を使用して遷移の操作を行うことができます。

```jsx
'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}
```

### メソッド

**push**
画面遷移時に使用します。

```jsx
router.push(href: string, { scroll: boolean })
```

**replace**
ブラウザに訪問履歴を残さずに遷移したい場合に使用します。

```jsx
router.replace(href: string, { scroll: boolean })
```

**refresh**
今いるページをrefreshし、再度サーバーに対してのリクエストを行うためのメソッドです。

```jsx
router.refresh()
```

**prefetch**
最初のセクションで説明したメソッドです。
useRouterで遷移する先に対するprefetchを行うために使用します。

```jsx
router.prefetch(href: string)
```

**back**
ブラウザ履歴内の前のページに遷移する際に使用します。

```jsx
router.back()
```

**forward**
ブラウザ履歴内の次のページに遷移する際に使用します。

```jsx
router.forward()
```

## LinkとuseRouterまとめ

- 特段の事由がない限りLinkを使用する (以下公式より)

```jsx
Recommendation: Use the <Link> component for navigation unless you have a specific requirement for using useRouter.
```

- aタグのようなリンクを作成したい場合はLlinkコンポーネントを使用し、内部的な動作（フォームの送信）やbuttonクリック時のコールバックなどにはuseRouterを使用する。
- useRouterはclient componentでのみ使用可能。

## 最後に

Laravelのbladeばっかり触ったいた僕からすると、勉強中でもnextのサクサク感に感動することが多いです。これからも頑張ります。
もし間違っていることやもっと書くべきことがあれば教えていただきたいです！

## 参考

https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating

https://nextjs.org/docs/app/api-reference/components/link

https://nextjs.org/docs/app/api-reference/functions/use-router