Open9
Next.jsメモ
AppRouter
- ルーティングを追加したい場合はapp配下にURLの名前でディレクトリを追加
- ディレクトリ配下に配置するファイル名は固有のファイル名がある。
- page, not-found, error, layout, loadingなど。
- ページへの初回アクセス時はサーバーから返却されたHTMLを表示する
- サイト内のリンクなどでページ遷移する際は、<Link>コンポーネントを使えばサーバーへのページ要求はとばず、SPAのように遷移する。
- app配下の
components
ディレクトリは固有のディレクトリ名で、ページとして認識されないため、この中に汎用コンポーネントなどを配置することができる。 - なお、nextのディレクトリ構成については以下を参照
動的ルーティング
- ディレクトリ名を
[ ]
で囲んで書くことで、blog/[id]
からクエリパラメータを受け取って表示内容を動的なページを作成できる。
blog
| -page.tsx
| - [id]
| - page.tsx
- ページ内でパラメータを受け取る方法は以下。
export default function SomePage({ params }: { params: { id: string } }) {
return (
<h1>{params.id}</h1>
);
}
- 検索用パラメータ(searchParams)を受け取ることも可能。同じページ内でUIを出し分けたい時などに使える。
export default function BlogPostPage({ searchParams }: { searchParams: { color: string } }) {
return (
<h1 style={{ color: searchParams.color }}>文字</h1>
);
}
favicon
- app配下に
icon.拡張子
の形式で画像を配置すればfaviconとして扱ってくれる。
RSC
- NextではデフォルトでReact server componentがビルド時に使用されるため、何もせずにそのままコンポーネントを定義するとサーバーサイドで生成されるコンポーネントとして定義される。
- ただ、useStateなどクライアントサイドで動作するフックスなどをコンポーネント内で使用する場合はclientSideコンポーネントとして描画する必要がある。
- その場合はファイルのトップレベルに
'use client'
と記載することで、CSRであることをNextに知らせる必要がある。 - コンポーネントをCSRにする場合、必要な部分のみをCSRにするようにコンポーネントを適切に切り出すことが重要。
データ取得
- NextではRSCをデフォルトで使用しているため、DBに直接アクセスしてデータを取得が可能。
- 素のReactだとデータフェッチングライブラリを使ったりuseEffectなどでAPIを実行してデータ取得することが一般的だが、Nextの場合はコンポーネント自体がサーバー内で実行されるため、コンポーネント自体を非同期関数として扱うことができ、コンポーネント内でawaitを使ってデータの取得が可能。
export default async function SomePage() { const items = await getItems(); // API実行 ...
- app配下のディレクトリにerrorという名前で配置すればNextがerror発生時のフォールバックとして認識してくれる。
- ただし、サーバー側のエラーもクライアント側のエラーも両方キャッチするため、"use client"をトップレベルに書かないとコンパイルエラーになる。
サーバーアクション
- 関数内に'use server'と記載することでサーバー側でのみ実行される関数を作成できる。
// 必ずasync functionとして扱う必要がある。
async function serverAction () {
"use server";
....
}
クライアントコンポーネントでサーバーアクションを使いたい場合
例えば以下のようなケースがあった場合、ビルドプロセスでエラーになってしまう。
sample.tsx
// コンポーネントをclientコンポーネントとして定義
"use client";
....
// サーバーアクションを定義
async function serverAction () {
"use server";
....
}
この場合はシンプルにサーバーアクションを別ふファイルに切り出してimportすれば共存可能。
sample.tsx
// コンポーネントをclientコンポーネントとして定義
"use client";
import { serverAction } from "@/lib/actions"
....
<button onClick={serverAction}>ボタン</button>
useActionState(useFormState)
- フォームの値とフォームアクションを返すhooks
- reactのcanaryリリースの機能であり、現行の安定バージョンには含まれていない。
- RSCをサポートするフレームワークで使用する。(Nextなど)
- 以前の
useFormState
の名前を変更したもの(現在はuseFormState
は非推奨) - なお、Reactでもcanaryリリースであり、Next公式でもuseFormStateを依然として使用しているため、Nextのプロジェクトにおいては、しばらくはuseFormStateを使うのが良さそう。
Interception route
- サイト内で画面AからBに遷移時、文字通り遷移途中でインターセプトして画面Cを表示する場合に使用。
- URLに対して直接アクセスがあったばあいはインターセプトは発生しない。
- 以下の通り
(..)photo
のように定義する。
- ここでは、
feed/photo/id
ページに直接アクセスがあった場合は矢印の通りに画面を表示するが、サイト内のリンクなどを通してfeed/photo/id
にアクセスがあった場合、(..)photo
の方が表示される。
Parallel Routes
- 同じレイアウト内に、複数ページを描画できたり、条件付きでレンダリングできます。
- 例えば画面内の2箇所に別々のAPIからデータ取得して内容を表示するコンポーネントがあった時など、それぞれでローディングやエラーのUIを制御したりできる。
- 他にも、Intercept routesと組み合わせれば、モーダルの作成も可能。
- SPAのモーダルと違って、モーダルの内容を URLをで共有でき、かつ、ページをリフレッシュした際でもモーダルの内容を保持できる。
Route group
-
app
ディレクトリ配下に(directoryName)
というディレクトリを書くことで、ディレクトリをページとして認識させずに論理的にディレクトリを整理できる。 - また、レイアウトが複数種類存在するサイトなどで、Route groupを使ってレイアウトが同じルートをまとめて定義することもできる。
キャッシュ
リクエストのメモ化
- Reactコンポーネントツリーのレンダリング中に、内容(configやエンドポイントなど)が同じAPIリクエストが複数回呼び出される場合、Nextが二回目のリクエスト以降は一回目の実行結果のキャッシュを返す仕組み。
- 利点
例えば、ルート全体(レイアウト、ページ、複数のコンポーネントなど)で同じデータを使用する必要がある場合、ツリーの先頭でデータをフェッチし、コンポーネント間でプロップを転送する必要はありません。その代わりに、同じデータに対してネットワークを介して複数のリクエストを行うことによるパフォーマンスを懸念することなく、必要なコンポーネントでデータをフェッチできます。
- Reactコンポーネントツリー内のfetchにのみ適用される。(Route HandlersはReactコンポーネントツリー内ではないので、適用されないので注意)
キャッシュ期間
Reactコンポーネントツリーが描画完了するまでの間
再検証(revalidation)
不要。(レンダリング中のみキャッシュされ、終わったらキャッシュが消えるため)
fetchAPI以外のAPIでリクエストのメモ化をする場合
Reactのcache関数を使う
import { cache } from 'react'
import db from '@/lib/db'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
データキャッシュ
- fetchAPIを使ってDBなどのデータソースから取得したデータを、Nextのサーバーでキャッシュする仕組み。
キャッシュ期間
ソース内で設定可能。revalidationかキャッシュ自体をoputoutしない限りは永続する。
再検証(revalidation)
再検証するまでの時間を設定するパターン
// 1時間ごとに再検証する
fetch('https://...', { next: { revalidate: 3600 } })
- 設定した時間内であれば同じ結果が返る。
- 設定した時間を超えたあと、最初のリクエストに対しては古いキャッシュを返し、裏では新しいデータにキャッシュが書き換わる。
- そのため、その次にリクエストすると新しいデータが返却される。
必要に応じて再検証を実行するパターン
- ソース内で
revalidatePath
orrevalidateTag
を呼び出して意図的にキャッシュを削除できる。その後に呼び出されるコンポーネントやAPIはデータソースからデータが再取得される。 - キャッシュが削除されるため、削除後の最初の再取得で新しいデータが取得/キャッシュされる。
fetchAPI以外のAPIでデータのキャッシュを行う場合
- Nextの
unstable_cache
を使用する。 - 第二引数にキャッシュキー
- 第三引数にfetchAPIの時と同じオプションを指定できる。
import { unstable_cache } from 'next/cache';
export const getData = unstable_cache(( )=> {
some Api call..
},
['some_cache_key'], // キャッシュキー
{
tags: ["some_tag"], // キャッシュ再検証用のタグ
revalidate: 3600, // キャッシュ再検証までの時間
}
);
Image
- Nextが用意するhtmlのimgタグを拡張したタグ
- デバイスに応じて画像サイズを最適化してくれる
- レイアウトシフト防止してくれる
- pngやjpegをそのまま使用するのではなく、Webpなど新しい画像形式を使用してくれる
- ViewPortに入ったときに描画するLazy Loadを設定してくれる。
- なお、画像が必ず画面内で表示されるもので、LazyLoadが必要ない場合は
priority
属性を付与することでLazy Loadしないようになり、preloadしてくれる。 - 画像のwidthやheightがわからず指定できない場合(画像データを動的に取得する場合など)、
fill
プロパティを使用する。ただし、fill
を指定したImageタグを親のタグでサイズ指定してやる必要がある。 - ローカルで保持している画像など、サイズが開発者からわかる場合や、画像サイズが固定の場合は
width
とheight
(もしくはsize
)を指定してあげることで画像の容量を小さくできる。
loaderプロパティ
-
loader
プロパティではImageタグが画像パスを内部的に生成するロジックを定義することができる。 - 例えば、外部のストレージなどから画像を取得している場合、Imageタグのsrcから画像のURLを取得して、URLをに必要な情報を加えてsrcに渡すことで、外部ストレージから必要なサイズの画像をもらうなどが可能。
function imageLoader(config) {
const [domain, imagePath] = config.src.split('/');
const someParams = "...." // 外部ストレージの画像リクエスト時のパラメータなど
return `${domain}${someParams}/${imagePath}`;
}
...
<Image
loader={imageLoader}
src={pathToImage}
alt="alt text"
width={100}
height={100}
/>
メタデータ
- layoutファイル内で
metadata
という定数名でexportする。 - これをNextが読み取ってheadタグを生成してくれる。
export const metadata = {
title: 'NextJS',
description: 'NextJS',
};
- メタデータを定義する場所はpageでもlayoutでも可能だが、layoutがおすすめ。layoutに書いたメタデータでpageのメタデータなどを上書きしてくれるため。
動的メタデータ
-
generateMetadata
関数を非同期関数としてエクスポートすることでNextが認識して動的にメタデータを設定してくれる。
export async function generateMetadata ({params}) {
const item = getItem(params.id)
if(!item){
// コンポーネントから一番近いnotFoundコンポーネントを呼び出す関数
notFound();
}
return {
title:...
description: ....
}
}
Warning: Extra attributes from the server: cz-shortcut-listen
- Nextでサーバーで生成されたコードとクライアントで表示されているコードに差があると出るワーニング
- chromeの拡張などで、コードにattributeなどが仕込まれるために発生していた様子。
- 今回の
cz-shortcut-listen
はColorZillaというページ内に使用されている色を知りたい時に使う拡張が原因だったよう。