Edited at
ClojureDay 10

Reagent から material-ui v3 を利用してハマった時に見る記事

この記事は Clojure Advent Calendar 2018 10日目の記事です。

前日の記事は @uochan さんの Vim で Clojure 開発するためのプラグイン vim-iced を作った話 でした。 cider に迫る多機能で便利なプラグインの作者直々の記事ですので、 Vimmer の方はぜひ一度ご覧ください :pray:

この記事は著者が material-ui v3 を使ってとあるwebアプリケーションを作っている中でハマった点をまとめたものです。

まだまだ開発途中なので今後も追記していきます。


前提条件

以下の構成のアプリケーションを想定しています。


props が変化する度に withStyles を利用して生成したコンポーネントが再マウントされてしまう

withStyles を利用したコンポーネントをレンダリングする度に再生成している場合、 withStyles から生成されるクラス名が再生成の度に変化するためコンポーネントが再マウントされてしまいます。

以下のように一度だけ withStyles を利用してコンポーネントを生成させるべきです。

Bad

(defn approach

[props]
[(r/adapt-react-class ((mui/with-styles approach-styles) (r/reactify-component approach-component))) props])

Good

(def approach

(r/adapt-react-class ((mui/with-styles approach-styles) (r/reactify-component approach-component))))

また、そもそもとして withStyles のような Higher-Order Components を利用してコンポーネントを生成する場合、 render メソッド (reagent で言う (defn hoge [props] [...]) 相当) 内での生成はコンポーネントの再マウントを招くのでReact 公式ドキュメントでも非推奨とされています。

このハマり方をした人はおそらくReagentのドキュメントを見てそれに習った方だと思うので、誤解のないようにドキュメントの修正PRを出しています。

↑この記事の投稿前にドキュメントの修正PRがマージされました :tada:


SSR を行っている場合、 hydrate 時にコンポーネントが再マウントされてしまう

公式ドキュメントに従ってSSRを行っていながらこの状態となる場合、サーバーサイドとフロントエンドで設定されている process.env.NODE_ENV が異なっていて生成されるクラス名が変化している可能性があります。

特に、通常 process.env.NODE_ENV はビルド時の環境変数によって変化するものですが、cljsjsのライブラリを利用している場合はその限りでなくライブラリに既に値が埋め込まれており、 :optimization が :none の時のみ 'development' 、それ以外は 'production'で固定 となります。

ここについては ClojureScript のドキュメントに書かれている通りで、読み込まれるライブラリが :optimization によって別のファイルになる事に起因するものなので、 process.env.NODE_ENV = 'production' でもコードは最適化しないと言った事は事実上不可能です。(そもそもここで議論されている通りサーバーサイドでもコードの最適化をしていなければパフォーマンス的に不利益を被るため、やること自体にメリットはないかも知れませんが)

つまり、本番用ビルドの場合はサーバーサイドも {:optimization :simple}{:optimization :advanced} に設定しておく必要があります。