NextJS は最近、13.4 バージョンをリリースし、App Router を「安定」させました。同時に、公式の CLI も App Router をデフォルトで推奨するように変更しましたが、App Router には React Server Components(以下 rsc と略す)の概念が導入され、多くの API が変更されました。これにより、学習の難易度がより高くなりました。
サーバーコンポーネント#
App Router では、NextJS は Client Components と Server Components を区別します。Server Components は特殊な React コンポーネントであり、ブラウザで実行されるのではなく、サーバーサイドでのみ実行されます。また、状態を持たないため、クライアント側にのみ存在する機能(つまり、useState、useEffect など)を使用することはできません。そのため、データの取得やコンポーネントのレンダリング(たとえば、マークダウンをレンダリングする場合、対応する JavaScript の依存関係はクライアント側にのみ存在します)に使用することが一般的です。これにより、クライアントのサイズを減らす効果があります。
同時に、App Router のファイルはデフォルトでサーバーコンポーネントになります。クライアントコンポーネントを使用する場合は、use client
を追加する必要がありますが、実際にはこのコマンドは子コンポーネントに影響を与えます。つまり、親コンポーネントにuse client
を追加した場合、このファイルのすべての子コンポーネントはこの指示を追加しなくてもクライアントコンポーネントになります。そのため、レイアウトを適切に計画し、クライアントコンポーネントをレイアウトから分離する必要があります。
以下の<MyComponent />
は実際にはクライアントコンポーネントです。
"use client";
import { useState } from "react";
import MyComponent from "./MyComponent";
export default function Home() {
const [num, setNum] = useState(0);
return (
<main>
<h1>{num}</h1>
<button onClick={() => setNum(num + 1)}>+1</button>
<MyComponent />
</main>
);
}
import { useEffect } from "react";
const MyComponent = () => {
useEffect(() => {
console.log("client component");
}, []);
return <div>123</div>;
};
export default MyComponent;
また、現在、多くのサードパーティライブラリはuse client
をサポートしていませんので、次のエラーが発生する場合があります。
rsc で正常に使用するためには、特別な処理が必要です。以下は framer-motion を例に挙げますが、通常、コンポーネントの外側にラップされます。
"use client"
import { FC, PropsWithChildren } from "react";
import { motion } from "framer-motion";
const MotionLayout: FC<PropsWithChildren> = ({ children }) => {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
);
};
export default MotionLayout;
または、次のようにカプセル化することもできます。
"use client"
import { motion } from "framer-motion";
export const MotionDiv = motion.div;
データの取得#
データのフェッチは rsc で重要な要素です。以下のように記述することができます。
export default async function Home() {
const data = await fetch("https://api.github.com/repos/vercel/next.js").then(
(res) => res.json()
);
return <div>{data.id}</div>;
}
ただし、これはデフォルトで SSG です。NextJS はネイティブの fetch をいくつか変更しているため、SSR に変更するにはcache
の設定を追加する必要があります。
export default async function Home() {
const data = await fetch("https://api.github.com/repos/vercel/next.js", {
cache: "no-store",
}).then((res) => res.json());
return <div>{data.id}</div>;
以下は ISR の書き方です。
// ISRは10秒ごとに再取得します
export default async function Home() {
const data = await fetch("https://api.github.com/repos/vercel/next.js", {
next: {
revalidate: 10,
},
}).then((res) => res.json());
return <div>{data.id}</div>;
}
axios などを使用して SSR を行いたい場合は、次のように記述することができます。
import axios from "axios";
export const dynamic = "force-dynamic";
export default async function Home() {
const { data } = await axios.get(
"https://api.github.com/repos/vercel/next.js"
);
return <div>{data.id}</div>;
}
ルーティング#
App Router のルーティングは以前と同様に強化されています。(folderName)
を使用してルーティングをグループ化することができます。括弧内のグループ名は実際のルートにはマッピングされませんが、コードの可読性を向上させるだけでなく、レイアウトを共有することもできます。
動的ルーティングに関しては、以前と同様に[folderName]
を使用して定義することができますが、props から直接対応する値を取得することもできます。
また、loading.tsx
を作成することもできます。これは Suspense をラップしたもので、page.tsx
でデータをフェッチする間に、loading.tsx
の内容を表示することができます。
同様に、error.tsx
もあります。ページのレンダリング中にエラーが発生した場合に、すぐにエラーページを表示することができます。
また、Parallel Routes
というものもあります。@folderName
をレイアウトの props にマッピングして、「スロット」として使用することができます。これは通常、ヘッダーやフッターなどのウェブページの上部や下部に適用されます。
export default function Layout(props: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
);
}
最後に#
App Router には非常に多くの API がありますが、ここで紹介したのはほんの一部です。詳細は公式ドキュメントを参照してください。
この記事はMix Spaceから xLog に同期して更新されました。
元のリンクは https://suemor.com/posts/programming/approuter です。