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