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)