Next.jsでパフォーマンスやUXを改善させるためにやったこと

# はじめに
最近Next.js（App router）を自習で触り始めて、自分のアプリが「なんか遅いなー」だったり、「使い心地悪いなー」と思うことが多々ありました。
そこで、今回は私がやったパフォーマンスやUXの改善方法について書いていきたいと思います。
# Linkコンポーネントにprefetchをつける
Linkコンポーネントにprefetchをつけてあげると、ビューポート内にLinkコンポーネントが入ると自動でページをprefetchしてくれます。
こうすることでページ遷移の速度が向上します。
```React
import Link from 'next/link'
 
export default function Page() {
  return (
    <Link href="/dashboard" prefetch={true}>
      Dashboard
    </Link>
  )
}
```
### useRouterの場合
また、useRouterにもprefetchの機能があるので、「リンクホバー時にprefetchしたい！」など要望があればこちらも使ってあげると良いでしょう。
```React
'use client';

import { useRouter } from 'next/navigation';

export default function PreloadExample() {
  const router = useRouter();

  // カーソルがボタンに入ったらprefetch
  const handleMouseEnter = () => {
    router.prefetch('/dashboard');
  };

  return (
    <button 
      onMouseEnter={handleMouseEnter}
      onClick={() => router.push('/dashboard')}
    >
      ダッシュボードへ
    </button>
  );
}
```
# ページ遷移時のプログレスバーをつける
デフォルトだとNext.jsにはページ遷移時のプログレスバーがないので、ライブラリを使ってプログレスバーをつけてあげると良いでしょう。
私の場合は[next-nprogress-bar](https://www.npmjs.com/package/next-nprogress-bar)を使って実装しました。
まずはライブラリをインストールしましょう。
```
npm install next-nprogress-bar
```
プログレスバーの設置方法としては、layout.tsxを`'use client'`にして直接プログレスバーを設置する方法と、プログレスバーのコンポーネントを分ける方法の2種類あります。
今回はプログレスバーのコンポーネントを分けて作っていきます。
```React:/components/ProgressBarProvider.tsx
"use client";

import { AppProgressBar as ProgressBar } from "next-nprogress-bar";

const ProgressBarProvider = ({ children }: { children: React.ReactNode }) => {
  return (
    <>
      {children}
      <ProgressBar
        height="2px"
        color="#2e8cfe"
        options={{ showSpinner: false }}
        shallowRouting
      />
    </>
  );
};

export default ProgressBarProvider;
```
次に上記で作ったコンポーネントをlayout.tsxに設置します。
```React:layout.tsx
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <ProgressBarProvider>{children}</ProgressBarProvider>
      </body>
    </html>
  );
}
```
これでページ遷移時にプログレスバーを表示させることができます。便利！

# クライアントでのデータフェッチにはライブラリを使う
### useEffect内でデータフェッチすると起こる問題
初心者がやりがちなことみたいなのですが、私は最初useEffect内でデータフェッチの処理を書いていました。
しかし、useEffect内でデータフェッチの処理を書くと**コンポーネントの初期レンダリング完了後にデータフェッチが開始されてしまいます**。
本来、データフェッチはレンダリング完了を待たずに行って欲しいので効率的ではありません。

### データフェッチライブラリを使おう！
SWRやTanStack Queryなどのデータフェッチングライブラリを使うことで、レンダリングとデータフェッチを同時に行うことができます。
今回はSWRを使用した例をご紹介します。

```React
// フェッチャー関数の定義
const fetcher = (url) => fetch(url).then((res) => res.json())

export default function Users() {
  // SWRフックを使用してデータを取得
  const { data, error, isLoading } = useSWR('/api/users', fetcher)

  // ローディング状態の処理
  if (isLoading) {
    return <div>データを読み込み中...</div>
  }

  // エラー状態の処理
  if (error) {
    return <div>エラーが発生しました: {error.message}</div>
  }

  // データの表示
  return (
    <div>
      <h1>ユーザー一覧</h1>
      <ul>
        {data.map((user) => (
          <li key={user.id}>
            {user.name} ({user.email})
          </li>
        ))}
      </ul>
    </div>
  )
}
```
### データフェッチングライブラリを使う他のメリット
データフェッチングライブラリを使うパフォーマンス上のメリットとしては以下も挙げられます。
- プリフェッチ
- キャッシュ
- 重複したリクエストの一本化

この中でもキャッシュしてくれることはパフォーマンス向上を実感できるレベルで使用前と差が出ます。
useEffectでfetchして「遅いなー」と感じている方は一度ライブラリを使ってみてはいかがでしょうか？

## できる限りサーバーコンポーネント内でデータフェッチする
先程はクライアントでのデータフェッチについて書きましたが、基本的にデータフェッチはサーバーコンポーネント内で行うことが好ましいです。
クライアントサイドでフェッチを行うとバンドルサイズが増加したり、ライブラリの学習コストがかかったりと色々デメリットがあります。
そのため、可能な場合はサーバーコンポーネント内でデータフェッチすると良いです。
詳しくは以下の記事からどうぞ。

https://zenn.dev/akfm/books/nextjs-basic-principle/viewer/part_1_server_components


# まとめ
今回は私が行ったパフォーマンス・UX向上のための施策をご紹介させていただきました。
まだまだ、できることはあるはずなのでこれからも自己研鑽していきます。
ここまで読んでいただきありがとうございました！

# 参考

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

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

https://zenn.dev/takumabuchi/articles/951f252ec3b9a5

https://zenn.dev/aidiot_dev/articles/20231026-useeffect-waterfall