🚀

【Next.js和訳】Basic Features/Layouts

4 min read

この記事について

株式会社 UnReact はプロジェクトの一環としてNext.js ドキュメントの和訳を行っています。

この記事は、Basic Features/Layoutsの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Layouts

Reactモデルでは、ページを一連のコンポーネントに分解することができます。これらのコンポーネントの多くは、ページ間で再利用されることがよくあります。例えば、すべてのページに同じナビゲーションバーとフッターがあるかもしれません。

components/layout.js
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

カスタムアプリによる単一の共有レイアウト

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

pages/_app.js
import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

ページ単位のレイアウト

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

pages/index.js
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return {
    /** コンテンツ */
  }
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // ページレベルで定義されたlayoutがある場合はそれを使用する
  const getLayout = Component.getLayout || ((page) => page)

  return getLayout(<Component {...pageProps} />)
}

ページ間を移動する際には、シングルページアプリケーション(SPA)の体験のために、ページのstate(入力値、スクロール位置など)を保持させます。

このレイアウトパターンでは、ページ遷移の間にReactのコンポーネントツリーが維持されるため、stateの永続化が可能になります。コンポーネントツリーにより、Reactはどの要素が変更されたかを理解し、stateを保持することができます。

Note

このプロセスはreconciliationと呼ばれ、Reactがどの要素が変更されたかを理解する方法です。

TypeScriptについて

TypeScriptを使用する場合は、まずページ用にgetLayout関数を含む新しいタイプを作成する必要があります。次に、AppProps用に新しいタイプを作成し、Componentプロパティをオーバーライドして、先に作成したタイプを使用する必要があります。

pages/index.tsx
import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return {
    /** コンテンツ */
  }
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.tsx
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'

type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  // ページレベルで定義されたlayoutがある場合はそれを使用する
  const getLayout = Component.getLayout ?? ((page) => page)

  return getLayout(<Component {...pageProps} />)
}

データのフェッチ

レイアウト内では、useEffectSWRなどのライブラリを使って、クライアントサイドでデータを取得することができます。このファイルはPageではないので、現在、getStaticPropsgetServerSidePropsは使用できません。

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

次にすべきことについては、以下のセクションをお勧めします。

https://nextjs.org/docs/basic-features/pages

https://nextjs.org/docs/advanced-features/custom-app

Discussion

ログインするとコメントできます