ReactのcomponentWillReceiveProps内ではまだpropsは反映されていない
とてつもなくはまったのでメモ。
componentWillReceiveProps
コンポーネントのプロパティ(props)は親コンポーネントにより任意のタイミングで変更されます。 その場合、componentWillReceivePropsが呼ばれるので、そこで新しいpropsの値を参照して、 それを基にコンポーネントの状態(state)を変更したり、その他の処理を行うことが可能です。
(入門React第3章より)
- こんな風に書かれてあるもんなので、てっきりcomponentWillReceiveProps内では送られたpropsが参照できると思っていました。
しかし、実際には、引数として指定してやらないと参照は出来ないようです。
例えば、以下のようにエンターキーを押すとテキストボックスに入力された文字を基に再描画が発生する親コンポーネントがあったとします。
//parent var Parent = React.createClass( { getInitialState() { return { value: "old value", }; }, sendCommand(e) { if(e.keyCode == 13){ var new_value = 'new value'; this.setState({value: new_value}); } }, render: function() { return ( <div> <Child value={this.state.value} /> <input type="text"onKeyDown={this.sendCommand} /> </div> ); } } );
Enterキーが押される度にsetStateが起きる、つまり子コンポーネントに新しいpropsが渡される
ということは子コンポーネントのcomponentWillReceivePropsが呼び出される。
ここまではよかった。
が、このメソッド内部のpropsは、古いまま(前回のコンポーネント作成時のままである)
//child var Child = React.createClass( { componentWillReceiveProps() { console.log(this.props.value); // => 'old value' }, .... } );
なので、入門Reactの通りこの内部で送られたpropsを使うには、以下のように引数としてpropsを受け取らなければいけない
//child var Child = React.createClass( { componentWillReceiveProps(nextProps) { console.log(nextProps.value); // ===> 'new value' console.log(this.props.value); // ===> 'old value' }, .... } );
setStateは即時反映されない問題
この問題はさらにややこしくしていたのが、setStateは即時反映(set)されるわけではないという謎の仕様
例えば上の親コンポーネントで例を示すと
//parent var Parent = React.createClass( { getInitialState() { return { value: "old value", }; }, sendCommand(e) { .... var new_value = 'new value'; this.setState({value: new_value}); console.log(this.state.value) // ==> old valueのまま! .... }, ..... } );
render内では反映された変更済みの値が参照できますが、this.setStateした時点では実際にはまだ更新はされていないようで、
その次の行とかで参照しても昔の値になってしまいます。
パフォーマンスなのか他の理由でこうしているのかはわかりませんが、これは結構否定意見が多かったらしく、
対応策としてthis.setStateの引数に関数を入れられるという対応がとられました。