Skip to content

Latest commit

 

History

History
112 lines (87 loc) · 3.57 KB

how-to-implement-hoc-with-nextjs.mdx

File metadata and controls

112 lines (87 loc) · 3.57 KB
title tags author slide published_at
Next.jsでのHOCの実装パターン
NextJS React
wawoon
false
2018-06-01

export const config = { amp: true }

モチベーション

  • Next.jsはgetInitialPropsというメソッドを定義することで、そのメソッドの返り値をコンポーネントのpropsを経由して渡すことができます
    • このgetInitialPropsは、SSR時であってもクライアントでもインスタンスの生成の前に呼ばれます(そしてgetInitialPropsの処理が完了したあとにページのconstructorにPropsとして渡される)
    • そのため、APIサーバーへのリクエストなどをしてSSRに必要なデータを取得するために使われる事が多いです。
  • 一方でHigh Order Component(以下HOC)をこのコンポーネントに適用しようすると、HOCによってページのコンポーネントがラップされてしまうため、子要素のgetInitialPropsが呼ばれず、コンポーネントに渡されるべきpropsが供給されないという問題が発生します。
  • この記事ではHOCを使っても子要素のgetInitialPropsが呼ばれるようにするためのHOCの書き方を紹介します。

解決策

  • Next.jsでHOCを書く場合は、返却されるコンポーネントにgetInitialPropsを実装する必要があります。
  • 新しく定義されたgetInitialPropsでやることは以下です
    • 子コンポーネントがgetInitialPropsを実装しているかチェック
    • もしそれが存在するならば実行し、returnします

実装例

シンプルな例

子コンポーネントをラップするだけ。 ログイン時のとき以外表示させないときにつかうHOC

import * as React from 'react'
import NotAuthorized from '../components/not_authorized'

interface Props {
  auth: {
    isAuthenticated: boolean;
    accessToken?: string;
    userId?: string;
  }
}

const withLogin = Page => class SecurePage extends React.Component<Props, {}> {
  static async getInitialProps (ctx) {
    // ポイントはここ
    // 引数にとったコンポーネントに、getInitialPropsが定義されているならばそれを実行する
    return Page.getInitialProps && ctx.auth.isAuthenticated && await Page.getInitialProps(ctx)
  }

  render () {
    const { isAuthenticated } = this.props.auth
    return isAuthenticated ? <Page {...this.props} /> : <NotAuthorized />
  }
}

export default withLogin

複雑な例

HOCを返す高階関数の場合

以下はレイアウトファイルを指定するHOC。

import {Component} from "react"

const withLayout = Layout => {
  return Page => {
    return class WrapperComponent extends Component<{layoutProps: {}, pageProps: {}}, {}> {
      static async getInitialProps(ctx) {
        let pageProps = {}
        let layoutProps = {}

        // Layoutで使うpropsがあればここで取得する
        if (Layout.getInitialProps) {
          layoutProps = await Layout.getInitialProps(ctx)
        }

        // Pageで使うpropsがあればここで取得する
        if (Page.getInitialProps) {
          pageProps = await Page.getInitialProps(ctx)
        }

        // 両者を合成してreturnする。
        return {
          ...layoutProps,
          ...pageProps
        }
      }

      render () {
        return (
          <Layout {...this.props}>
            <Page {...this.props} />
          </Layout>
        )
      }
    }
  }
}

export default withLayout;

使い方の例

withLayout(AdminLayout)(Page)