Open6

Pages and Layouts

あおけんあおけん

ファイルがpagesディレクトリに追加されると、自動的にルートとして利用できる。

例えば、pages/about.jsを作成すると、/aboutからアクセスできるようになる。

export defautl function About() {
  return <div>Aboud</div>
}
あおけんあおけん

Index routes

ルーターは自動的にindexという名前のファイルを、
ディレクトリのルートにルーティングする。

  • pages/index.js → /
  • pages/blog/index.js → /blog
あおけんあおけん

Nested routes

ルーターはネストされたファイルをサポートする。
ネストされたフォルダ構造を作成した場合でも、
ファイルは自動的に同じ方法でルーティングされます。

  • pages/blog/first-post.js → /blog/first-post
  • pages/dashboard/settings/username.js → /dashboard/settings/username
あおけんあおけん

Layout Pattern

Reactモデルでは、ページを一連のコンポーネントに分解することができる。
これらのコンポーネントの多くは、ページ間で再利用されることが多い。
例えば、どのページにも同じナビゲーション・バーやフッターがあるような場合。

// components/layout.js
import Navbar from './navbar'
import Footer from './footer'
 
export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}
あおけんあおけん

Examples

Single Shared Layout with Custom App

アプリケーション全体のレイアウトが1つしかない場合、
カスタムアプリケーションを作成し、そのレイアウトでアプリケーションをラップすることができる。
<Layout />コンポーネントはページを変更するときに再利用されるので、
コンポーネントの状態は保持されます(入力値など)。

// pages/_app.js
import Layout from '../components/layout'
 
export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

Per-Page Layouts

複数のレイアウトが必要な場合は、ページにgetLayoutプロパティを追加して、
レイアウト用のReactコンポーネントを返すことができる。
これにより、ページごとにレイアウトを定義することができる。
関数を返すので、必要であれば複雑なネストしたレイアウトも可能。

// pages/index.js
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
 
export default function Page() {
  return (
    /** Your content */
  )
}
 
Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
// pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // Use the layout defined at the page level, if available
  const getLayout = Component.getLayout ?? ((page) => page)
 
  return getLayout(<Component {...pageProps} />)
}

ページ間を移動するとき、SPA体験のために、
ページの状態(入力値、スクロール位置など)を永続化したい。

このレイアウト・パターンは、
Reactのコンポーネント・ツリーがページ遷移の間維持されるため、
状態の永続化を可能にする。

コンポーネント・ツリーによって、
Reactは状態を保持するためにどの要素が変更されたかを理解することができる。

With TypeScript

TypeScriptを使用する場合、
まずgetLayout関数を含む新しい型をページ用に作成する必要があります。

次に、AppProps用に新しい型を作成し、
Componentプロパティをオーバーライドして、
先に作成した型を使用するようにする。

// pages/index.tsx
import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'
 
const Page: NextPageWithLayout = () => {
  return <p>hello world</p>
}
 
Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
 
export default Page
// pages/_app.tsx
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
 
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode
}
 
type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}
 
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  // Use the layout defined at the page level, if available
  const getLayout = Component.getLayout ?? ((page) => page)
 
  return getLayout(<Component {...pageProps} />)
}

Data Fetching

レイアウトの内部では、useEffectやSWRのようなライブラリを使用してクライアントサイドでデータを取得することができます。
このファイルはPageではないので、
現在getStaticPropsやgetServerSidePropsは使用できません。

// components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'
 
export default function Layout({ children }) {
  const { data, error } = useSWR('/api/navigation', fetcher)
 
  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>
 
  return (
    <>
      <Navbar links={data.links} />
      <main>{children}</main>
      <Footer />
    </>
  )
}