https://facebook.github.io/react/docs/tutorial-ja-JP.html を見ながらReactをとりあえず触ってみたのでメモ。
コンポーネントを作る
コンポーネントを作って、そのrenderメソッドでどういう構造を描画するかJSXで記述しておいて、描画を変更したい時にstateを変えると、いい感じに描画してくれる、というだけっぽかった。
例えば指定した時間ごとにカウントアップするコンポーネントを作るとすると、以下の様に書いたらいい。
var CountUp = React.createClass({ getInitialState: function () { // 初期状態を定義 return { count: 0 }; }, componentDidMount: function () { // コンポーネントが最初にレンダリングされた後に実行される // 渡されたintervalミリ秒ごとにincrementメソッドを呼ぶだけ setInterval(this.increment, this.props.interval); }, render: function () { // 描画する構造の定義 return ( <span className="countUp">{this.state.count}</span> ); }, increment: function () { var count = this.state.count; this.setState({ count: count + 1 }); } }); ReactDOM.render( // CountUpコンポーネントが1000msごとにcountupするように1000を渡している <CountUp interval={1000} />, document.getElementById('content') );
あとは単にコンポーネントはネストできるからいい感じにしてください、ってだけみたい。自分で構造をどんどん弄る必要がなくて楽ですね。
親コンポーネントと子コンポーネントのデータの受け渡し
親コンポーネント -> 子コンポーネントへはプロパティっぽく渡すとデータを受けわたせる
<CountUp interval={1000} />
こんな感じで渡すと、子コンポーネントの中で、this.props.intervalが取れるだけ。
子コンポーネント -> 親コンポーネントにデータを渡す時も同じ仕組みしか使ってなさそう。親コンポーネントから子コンポーネントにcallbackのメソッドを渡して、子コンポーネントからそれを呼ぶだけ。この辺はチュートリアルを見るとわかりやすそう。
setIntervalにthis.incrementを渡してなんで大丈夫なのか
チュートリアル通りにやっていて、疑問に思ったのが、createClassのfunction内で以下のように書いてなんでうまくいくのかというところだった。
setInterval(this.increment, 1000);
普通に考えるとsetIntervalにfunctionを渡すと、そのfunction内のthisはglobalオブジェクトになりそうだけどなんでならないのか。コードを追いかけると、createClassは内部的に自動で関数をbindしてくれているみたい。
- mixSpecIntoComponentにcreateClassに渡したオブジェクトが渡されて
- React用でない関数shouldAutoBindフラグが立って
- __reactAutoBindPairsというところに入れられて
- bindAutoBindMethodsが呼ばれて
- それぞれbindAutoBindMethodが呼ばれて
- メソッドがbindされる
また、https://github.com/facebook/react/blob/38900cc7cad8906c19515c78fd082637c2046fbd/src/isomorphic/classic/class/ReactClass.js#L492 を見ると、autobindプロパティfalseを渡すとこの挙動をなくすことができるので、先ほどのコードはこうも書ける。
var CountUp = React.createClass({ getInitialState: function () { // 初期状態を定義 return { count: 0 }; }, componentDidMount: function () { // コンポーネントが最初にレンダリングされた後に実行される // 渡されたintervalミリ秒ごとにincrementメソッドを呼ぶだけ setInterval(this.increment.bind(this), this.props.interval); }, render: function () { // 描画の定義 return ( <span className="countUp">{this.state.count}</span> ); }, increment: function () { var count = this.state.count; this.setState({ count: count + 1 }); }, autobind: false });