Next.jsとMaterial-UIの組み合わせでビルドすると予期せぬ表示ずれがあった。
現象としてはnext.jsのPHASE_PRODUCTION_BUILD
でgetInitialProps()
のあるページで初回読み込みをするとmakeStyles
によるスタイルが崩れる。
ErrorやWarningはないし上記以外のアクセスではスタイル崩れもない。
結構複雑な問題だったので要点をまとめながら解決を図ります。
環境:next: 9.1.7material-ui: 4.8.3react: 16.12.0カスタムサーバー express: ^4.17.1
推察
next.jsとMaterial UIによる問題で以前クラス名が合わないエラーがあった。
初期アクセスで問題が起きるのでライフサイクルも考えると_document
が怪しい。
ビルドの有無で変わることからコンパイルの処理も見る必要がありそう。
該当箇所の確認
Appbar、Toolbarの独自スタイルがちゃんと効いていない。
1 2 3 4 5 6 7 8 |
const useStyles = makeStyles(theme => ({ appBar: { zIndex: theme.zIndex.drawer + 1, backgroundColor: "#2e8b57" } })); <AppBar position="fixed" className={classes.appBar} color="primary"> |
この書き方以外でclasses
のroot
などを書き換える方法も試したが同じだった。
上記コードで出来るのは.makeStyles-appBar-4
だったが、そのクラス自体はちゃんと定義されている。
ただそれより上位に.MuiAppBar-colorPrimary
などがあることでおかしくなってる。
原因の深堀
- 1.
PHASE_DEVELOPMENT_SERVER
では期待通り - 2.
PHASE_PRODUCTION_BUILD
でgetInitialProps
のないページを表示後、内部遷移でgetInitialProps
のあるページに移動すると期待通り - 3.
PHASE_PRODUCTION_BUILD
でgetInitialProps
のあるページを初回表示時、makeStyles
が適応されない状態になる
2と3のページソースを見ても変化はない。
ディベロッパーツールで要素を見てみる。
- MuiPaper-root MuiAppBar-root MuiAppBar-positionFixed makeStyles-appBar-4 MuiAppBar-colorPrimary mui-fixed MuiPaper-elevation4
- MuiPaper-root MuiAppBar-root MuiAppBar-positionFixed jss4 MuiAppBar-colorPrimary mui-fixed MuiPaper-elevation4
- MuiPaper-root MuiAppBar-root MuiAppBar-positionFixed makeStyles-appBar-4 MuiAppBar-colorPrimary mui-fixed MuiPaper-elevation4
buildするとmakeStyles
によるクラスが.jss
として再定義されるが3の状態だと元のままになっているのが問題だと考えられる。
またgetInitialProps
内で非同期処理をしない(return {}
するだけ)場合でも解決しなかったのでSSR(サーバーサイドレンダリング)の有無が問題に見える。
解決案
ここまで問題を見てきて解決方法は2つ思いつきます。
-
makeStyles-appBar-4
は存在するんだから上位に設定する - SSRするページのクラスも
jss
になるようにコンパイラをいじる
1はスタイルがdevと同じように設定されるように微調整を行うことになります。
一番簡単な方法はmakeStyles
内の値に!important
を付けること。
2はこのサンプルやwith-react-jssを見るとJssProvider
でどうにかしようとしていますが、material uiのバージョンとreact-jssのバージョンの違いでうまくいかない感じになります(generateClassNameの再定義が出来ない)。
うーん。
そもそもの原因は?
対処療法に傾きつつあったのでnextかreactかmaterial ui(jss)か何が悪いのか。
色々探索する中でMaterial UIのソースを見てるとif (process.env.NODE_ENV !== 'production')
が所々にあることに気づいた。
試しにNODE_ENV
をproduction
にするとSSRするページでも.jss
クラスが付与されることを確認できた。
複数環境での動作のためにNODE_ENV=production1
とかにしていたのが裏目だったか。
まさかこんなことでこんなに悩むとは。
しかしNODE_ENV
で動作が変わるのってどうなんだろう…。
実際に処理でNODE_ENV
分岐をしてるのが誰(どこ)なのかはわかりませんが、疲れたのでとりあえずbuild&start
するときはNODE_ENV
をproduction
に。
同じ問題にハマり、原因は追いきれなかったのですが、そもそもnext.jsはNODE_ENVを書き換えることに対応していないという話でした。
https://github.com/vercel/next.js/issues/19046