Open9

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>
  )
}
いもけんいもけん

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が使用できる

使い分け

https://beta.nextjs.org/docs/rendering/server-and-client-components#when-to-use-server-vs-client-components

いもけんいもけん

インポートルール

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>
  )
}