Open9
Next.js 13 実践
Next.js 13基本
layout.tsx
layoutファイルにはhtmlタグとbodyタグを含める必要がある
import '../styles/globals.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>{children}</body>
</html>
)
}
MetaData
head.js(head.jsx)がベータ版の段階では存在したが、無くなった
代わりにStatic Metadataというものができた
詳細
env
NEXT_PUBLIC_SUPABASE_ANON_KEY=
apikey=
NEXT_PUBLICをつけるとクライアントで使用することができるがバンドルに含まれてしまうので、完全に値をシークレットにすることはできない
NEXT_PUBLICをつけないとクライアントからは読み込めないがサーバー側からは読み込むことができる
つまりサーバーコンポーネントを用いることで完全にシークレットな環境変数を使用することができる
サーバーコンポーネントだとコンポーネントレベルでasync/awitがそのまま使える
(クライアントコンポーネント(従来)だと、useEffectやTanStackQuery,SWRなどのサードパーティのライブラリを使う必要があった)
async function fetchNotes() {
// 挙動がわかりやすいように意図的に2秒間遅延
await new Promise((resolve) => setTimeout(resolve, 2000))
const res = await fetch(`${process.env.url}/reset/v1/notes?select=*`, {
headers: new Headers({
apiKey: process.env.apiKey as string,
}),
})
if (!res.ok) {
throw new Error('Failed to fetch data in server')
}
const notes: Note[] = await res.json()
return notes
}
export default async function NotesList() {
const notes = await fetchNotes()
return <div></div>
}
cache option
fetch(URL, { cache: 'force-cache' });
デフォルトのオプションgetStaticPropsの挙動(HTMLがCDNにキャッシュされる)
fetch(URL, { cache: 'no-dtore' });
getServerSidePropsの挙動
fetch(URL, { next: { revalidate: 10 } });
ISRの挙動
loading, error
appDirではファイルを作成することで自動的に以下のようなjsxを作成してくれる
<Layout> {/* layout.tsx */}
<ErrorBoundary fallback={<Error />}> {/* error.tsx */}
<Suspense fallback={<Loading />}> {/* loading.tsx */}
<Page /> {/* page.tsx */}
</Suspense>
</ErrorBoundary>
<Layout>
- error.tsxの内容はクライアント表示する必要があるのでファイルの最初に
'use client'
をつける必要がある
比較
サーバーコンポーネント特徴
- サーバーでレンダリングされる -> jsはclientに送られない
- コンポーネントレベルでasync functionを使用できるのでデータフェッチの記述が楽
- シークレットキーが利用できる
- BrowserAPIは使用できない
- useState, useEffect等は使用できない
- onClickのようなEventlistenerは使用できない
クライアントコンポーネント特徴
- ブラウザでjsが実行させる
- コンポーネントレベルでasync functionを使用できない -> useEffect.tanStackQuery, useなどを使用する必要がある
- シークレットキーの使用ができない
- useState, useEffect等が使用できる
- onClickのようなEventlistenerが使用できる
使い分け
インポートルール
appDirで作られるファイルはデフォルトでサーバーコンポーネントになっている
サーバーコンポーネントでクライアントコンポーネントをインポートすることは可能だが
クライアントコンポーネントでサーバーコンポーネントをインポートすることは不可能
例外として、クライアントコンポーネントのchildrenとしてサーバーコンポーネントを渡すことは可能
import NotesList from './components/notes-list'
import TimeCounter from './components/timer-counter'
export default function Page() {
return (
<main>
<div className="m-10 text-center">
<p> Hello World</p>
{/* @ts-ignore */}
<NotesList /> {* サーバーコンポーネント *}
<TimeCounter /> {* クライアントコンポーネント *}
</div>
</main>
)
}