NextJS は最近、13.4 バージョンをリリースし、App Router を「安定」させました。同時に、公式の CLI も App Router をデフォルトで推奨するものに変更しましたが、App Router では React Server Components(以下、rsc)の概念が導入され、多くの API が変更されました。これにより、学習の難易度が高くなりました。
サーバーコンポーネント#
App Router では、NextJS はクライアントコンポーネントとサーバーコンポーネントを区別します。サーバーコンポーネントは特殊な 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です。