react-intlが動作しなかった
material-ui Ver0.16.7の話です(恐らくver関係ないと思いますが・・・)
material-uiの<TextForm />
を使用していた時のこと。
<TextForm />
のprops
にerrorText
というものがあり、そこにreact-intlの<FormattedMessage />
を使用していました。
<TextField
errorText={<FormattedMessage id={'hoge'}/>}
hintText={<FormattedMessage id={'hogehoge'}/>}
/>
という風に使用していました。
言語切り替えようのボタンを押すと、言語部分が再レンダリングされるという感じです。
しかし、なぜか私が作成した一部分だけが言語が切り替わっていませんでした。
原因
フォームにエラーメッセージを設定する際は、すでに構築されているフォームにエラーメッセージなどの更新処理を施すようにしていたのですが、そこが原因でした。
ComponentWillMount() {
// stateにフォームの内容を設定
フォーム構築関数();
}
ComponentWillReceiveProps() {
// setState()で更新処理
すでに構築されたフォームに対して更新処理();
}
render() {
return {
<FormattedMessage id={'test'}/> // これは切り替わる
<独自Component
data={this.stateに設定したformの内容}
/>
}
}
言語を切り替えた際は、ComponentWillReceiveProps
が動作するのですが、その際<TextForm />
の内部で再レンダリングするかどうかを判断する処理が動き、結果再レンダリングさせないと判断されて言語が切り替わっていませんでした。
reactのライフサイクルには、shouldComponentUpdate
というものがあります。
ここでfalse
が返されると、再レンダリングが実施されないというものです。
もうお気づきかと思いますが、<TextForm />
の中でこの処理が動作して、false
が返されていたわけです。
<TextForm />
はなぜ再レンダリングしないと判断したか
material-uiはパフォーマンスのためか、shouldComponentUpdate
の中で変更前と変更後の一致比較を行います。一致すれば再レンダリングを行いません。
言語を切り替えた際、<FormattedMessage />
に変更はないのでshouldComponentUpdate
の中で一致してしまうわけです。(恐らく言語ファイルの参照先を帰るだけだから変更が起きないのかと・・・)
対応策
ベストな対応策では無いと思いますが、私はformをレンダリングのたびに再構築させることにしました。
render() {
const form = {
// formの内容
}
return {
<独自Component
data={form}
/>
}
}
material-uiの一致比較の際、インスタンスが別であれば中身が同じであっても別物と判断されるのを利用しています。
今のところパフォーマンスに影響も出ていないので、問題無いかな・・・?
原因特定が難しくて、胃が痛かったです・・・
(原因がわかると理由は簡単なものでしたが、<FormattedMessage />
単体はしっかり動作するので特定がかなり難しかったです・・・)