モチベーション
- 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
1import * as React from 'react'2import NotAuthorized from '../components/not_authorized'34interface Props {5 auth: {6 isAuthenticated: boolean;7 accessToken?: string;8 userId?: string;9 }10}1112const withLogin = Page => class SecurePage extends React.Component<Props, {}> {13 static async getInitialProps (ctx) {14 // ポイントはここ15 // 引数にとったコンポーネントに、getInitialPropsが定義されているならばそれを実行する16 return Page.getInitialProps && ctx.auth.isAuthenticated && await Page.getInitialProps(ctx)17 }1819 render () {20 const { isAuthenticated } = this.props.auth21 return isAuthenticated ? <Page {...this.props} /> : <NotAuthorized />22 }23}2425export default withLogin26
複雑な例
HOCを返す高階関数の場合
以下はレイアウトファイルを指定するHOC。
1import {Component} from "react"23const withLayout = Layout => {4 return Page => {5 return class WrapperComponent extends Component<{layoutProps: {}, pageProps: {}}, {}> {6 static async getInitialProps(ctx) {7 let pageProps = {}8 let layoutProps = {}910 // Layoutで使うpropsがあればここで取得する11 if (Layout.getInitialProps) {12 layoutProps = await Layout.getInitialProps(ctx)13 }1415 // Pageで使うpropsがあればここで取得する16 if (Page.getInitialProps) {17 pageProps = await Page.getInitialProps(ctx)18 }1920 // 両者を合成してreturnする。21 return {22 ...layoutProps,23 ...pageProps24 }25 }2627 render () {28 return (29 <Layout {...this.props}>30 <Page {...this.props} />31 </Layout>32 )33 }34 }35 }36}3738export default withLayout;39
使い方の例
1withLayout(AdminLayout)(Page)2