Reactコンポーネントをファイル分割したら沈黙してしまった件
Reactで大きな画面を作るうちに、人の習性としてReactコンポーネントをファイル分割したくなります。
var React = require('react'); var Item = React.createClass({ render: function () { return <li value={this.props.value}>{this.props.text}</li>; } }); var List = React.createClass({ render: function() { var items = this.props.data.map(function(i) { return <Item {...i}/> }); return ( <ol>{items}</ol> ); } }); var data = [ {text: 'One', value: 1}, {text: 'Four', value: 4}, {text: 'Five', value: 5}, {text: 'Seven', value: 7} ]; var ReactDOM = require('react-dom'); ReactDOM.render(<List data={data}/>, document.getElementById('List'));
と、まあこんなサンプルがあったとして、気持ち悪いのはコンポーネントの宣言記述と、その利用が同じファイルでやってること。Reactの説明で多くの場合はコンポーネントの宣言直下でReactDOM.render()を行ってますが、まあ実践では違う作りになるんじゃないかなと。で、以下みたいにするとします。
- コンポーネント(List.jsx)これは動かないダメな例!
// List.jsx var React = require('react'); var Item = React.createClass({ // 省略 }); module.exports = List = React.createClass({ // 省略 });
- 描画部の別ファイル
var data = [ // 省略 ]; var ReactDOM = require('react-dom'); var List = require('./List.jsx'); ReactDOM.render(<List data={data}/>, document.getElementById('List'));
汎用たるコンポーネントと、個別たる描画利用部が分かれていい感じ。requireもreactとreact-domがそれぞれで、なるほどこれがデザインかと。しかしこれらをBrowserifyでまとめたらさっきまで動いていたのに沈黙してしまいました。
しばらく嵌るうちにわかったのは、Listコンポーネントがexportされているけど、その中で使われているItemコンポーネントがexportされていないから。全部同じファイルだったら見えるし、描画-List-Itemと三つのファイルに分割する(もちろんItemも適切にexportする)のならば動きます。ただし描画部で、ReactDOM.render()の引数に渡しているJSXがTranspileした結果、React.createElement(List, {data: data})と変換されているので、Reactをコード上でてこなくてもrequireしなくちゃならなそうだったりと新たな痒さも。
コンパイラ言語だったり、JavaScriptでもクロージャーみたいに、スコープ見えるはずと思っていてもBrowserifyによる力技なコピーかき集め作業の前には通用しなかった。Reactコンポーネントはファイル毎にひとつづつってのがReact-Lintも警告するような常識だったようですが、かといって細かいファイルたくさんは作りたくない。。。はてさて。