react.js v0.13 RC – setStateで関数を渡せるようになった

このエントリーをはてなブックマークに追加

this.setState() can now take a function as the first argument for transactional state updates, such as this.setState((state, props) => ({count: state.count + 1})); — this means that you no longer need to use this._pendingState, which is now gone.

React.js v0.13rc になってthis.setState()が第一引数にfunctionを受け付けることができるようになりました。これは結構うれしいです。今まで一つの関数内で複数回に渡ってthis.stateの中身をsetStateで変更したとしてもthis.stateの変更が瞬時に反映されませんでした。ちょっとよくわからない日本語になっているので実際にコードと結果を体験してみるとわかります。

上のコードの様にsetStateを同一関数内(handleClick)で呼び出してthis.state.countの中身を変えたとしても、2回目のthis.state.countの時点では、一回目のsetStateの変更が反映されていない為、両者とも「0 + 1」を行っていることになり、結果的に一度しかthis.state.count + 1をやっていないように動作してしまいます。

「this.state.count」を直接変えればいいじゃん?「this.state.count = this.state.count + 1」ってさ

と、思う人もいるかもしれませんが、確かに即時反映はされるのですがReactではthis.stateにひっついているstateを変えることは絶対にしてはいけません。内部のロジックまでは私はわかっていませんが、this.stateの変更を参照することで再描画の必要性などが把握できているのではないかと思います。なので直接「this.state.状態」は触ってはいけません。これには例外があって、直接this.stateにひっついているstateを変えてはいけないだけであって、例えば以下のようなstateを変更することは特に禁じられていないです。

// this.stateの直接の子を変更してしまっているのでNG
this.state.count = 0;

// this.stateの直接の子状態ではないのでOK
this.state.nestedObject.count = 0;

上の対策としてv0.13rc以前は’this._pendingState’を使う方法がありました。これは、変更予定のあるstateの最新状態を管理しているプロパティです。上記のコードを意図した通りに動作させたい場合は以下のように書くことができるのです。

しかし、プロパティ名である’_pendingState’が「__」で始まっているのが観てもわかるように、これは内部的に使用されている変数であって、React的にはpublicにしたくないプロパティだったのではないかと思われます。(不本意に使われる羽目になってしまった)

この問題は結構熱くGithub上でも議論がされていて、例えば以下のIssueやPullがそれぞれ関連してそうです。

結果的にv0.13rcではsetStateの第一引数に関数を与えることができるようになったのです。v0.13rc以降は関数を使ってより高度にstateに副作用を与えることができるようになります。

var Hello = React.createClass({
    getInitialState: function(){
      return {count:0};
    },
    handleClick: function(e) {
      // you can now pass function to 'setState' and retrieve the most up to date state value
      this.setState(function(state,props) {return {count: state.count + 1};});
      // this will increment 'state.count' by 1. Which means it will add 2 in total
      this.setState(function(state,props) {return {count: state.count + 1};});
    },
    render: function() {
      return <div onClick={this.handleClick}>Count: {this.state.count}</div>;
    }
  });

  React.render(<Hello />, document.body);

 

これは柔軟性が高まりそうな変更ですね!

Written on March 4, 2015
このエントリーをはてなブックマークに追加