Edited at
Next.jsDay 4

Next.jsの_app.jsと_document.js

Next.jsで基本的な挙動が理解できたら、次に考えることは実践で使う細かい設定だ思います。つまり、ページ全体の共通の設定や、状態管理、エラーハンドリング(これは明日のアドベントカレンダーでまとめます。)などです。

全体設定のためのインターフェイスとしてNext.jsが用意しているのが、pages/_app.jspages/_document.jsになります。

それぞれの役割と実際に使われる場面を見ていきましょう。

一部、公式ドキュメントを翻訳しています。

https://nextjs.org/docs/#custom-app


_app.js

まずはpages/_app.jsのサンプルです:


pages/_app.js

import React from 'react'

import App, { Container } from 'next/app'

export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}

if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}

return { pageProps }
}

render () {
const { Component, pageProps } = this.props

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


Next.jsはすべてのページにおいて、ページを初期化するためにAppコンポーネントを使用しています。pages/_app.jsファイルでAppコンポーネントをオーバーライドすることで、ページの初期化をコントロールできます。

つまりは以下の設定が可能になります:

全ページで必要な挙動を書ける場所なので、他にも広告まわりの関数の実行や、NProgressなどのローディングを設定したりしています。

注意すべき点はNext.jsが用意するページコンポーネントと同じ挙動を取るので、_app.jsはサーバーサイドでレンダリング(getInitialPropsの実行を含む)され、ライフサイクルのイベントはクライアントサイドでも実行されます。


_document.js

まずはpages/_document.jsのサンプルです:


pages/_document.js

import Document, { Head, Main, NextScript } from 'next/document'

export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}

render() {
return (
<html>
<Head>
<style>{`body { margin: 0 } /* custom!*/`}</style>
</Head>
<body className="custom_class">
<Main />
<NextScript />
</body>
</html>
)
}
}


Next.jsのページでは<html><body>などの基本的なマークアップはデフォルトで書かなくてよい設定になっています。もし全体の文書構造をカスタマイズしたいのならpages/_document.jsファイルで<Document>コンポーネントを拡張しましょう。

<head>タグ内の記述だけでなく、styled-componentsをSSR時に<head>に埋め込む処理なども書けます。

注意すべき点は_document.jsはサーバーサイドのみでレンダリングされ、クライアントサイドでは使われません。onClickのようなイベントハンドラはここに追加しないでください。すべてのページで共通のコンポーネントは<App>コンポーネントに書くようにしましょう。


おわりに

Next.jsを実践で使う第一歩としての_app.jsと_document.jsを取り扱いました。明日はエラーハンドリングまわりをまとめます。