react-provider
github: https://github.com/morisuke/react-provider
npm: https://www.npmjs.com/package/react-provider
推奨コンパイル環境: Browserify + Babelify
動作環境: IE8+
Q. なにこれ?
A. HTML要素をReact.jsコンポーネントに置き換えるよ
// ES6 Class Component
class Component extends React.Component {
render() {
return (
<div className={this.props.className}>
{this.props.children}
</div>
);
}
}
// Browserifyで読み込み
var provider = require('react-provider');
// selector: Reactコンポーネント
provider({
'[data-component=component]': <Component />
});
<body>
<div data-component="component" class="wrapper">
wrapper-text
<div data-component="component" class="inner">inner-text</div>
</div>
<script src="./app.js" type="text/javascript">
</body>
<body>
<component>
<div class="wrapper" data-reactid=".0">
wrapper-text
<component>
<div class="inner" data-reactid=".1">inner-text</div>
</component>
</div>
</component>
<script src="./app.js" type="text/javascript">
</body>
Q. どうしておとなしくVue.jsにしなかったんだ
A. IE8の闇に呑まれた。
「babelでIE8対応かーすごいなーあこがれちゃうなー」と高みの見物してたら自分がやる羽目になって血反吐吐いてる
— morisuke (@morisuke_tec) 2015, 5月 1
こんな人の役に立つかも
- __Vue.js大好き__なんだけど__IE8__に対応しなければいけなくなった → なんとなく同じように使えるよ
- Reactが好きでたまらない他のものは触りたくない、だがしかし作るものはSPAではない → 頭を冷やせ
開発の動機
React.jsはReact.render
を叩くことで画面上にコンポーネントを描画します。
しかしReact自体がSPAを構築する前提で組まれていることもあり、React.render
の第二引数に指定する描画対象要素は単独のnodeでしか渡せない仕様になっています。(nodeListを渡せない)
しかしPHPのビューテンプレートと併用する場合、どうしても複数のコンポーネントを同時に画面に描画したくなります。(フォームヘルパの機能を拡張したい場合など)
それならnodeListをquerySelectorAllで拾って回しながら描画し続ければ良くない? という発想から生まれたのがこのライブラリです。
実装の方針
やりたいことにほぼ合致しているreact-mountというライブラリがあったのですが、ほぼ正規表現による実装であるため手を加えにくく、またIE10で何故か動かなかったので、自分でイチから実装することにしました。
react-mountはJSXをHTML上に書けるようにしていましたが、こちらはあくまでロジックをReactコンポーネントに隠蔽したい考えであった為、this.props.children
に子要素をReactElementで渡すのみに留めています。
コンポーネントをネストさせて記述した場合に、refsにツリー構造が格納されるようにしようとも思ったのですが、HTML上の記述とコンポーネントの実装に強い依存関係が生まれるため、途中でやめました。
なのでコンポーネント間の通信にはシングルトン化したEventEmitterなどを利用します。
ここらへんはVue.jsの$on / $broadcast
のイメージで組んでいくとわかりやすいと思います。
無駄に力入れたところ
画面上からpropsに値やReactElementを渡せるようにしました。
data-props もしくは data-props-html 属性を指定した要素の中身をコンポーネントに吸い上げます。
<body>
<component class="wrapper">
<div data-props="element">
textnodeです
</div>
<div data-props-html="element">
<span class="style">htmlです</span>
</div>
</component>
<script src="./app.js" type="text/javascript">
</body>
こうするとthis.props.element
に、textnodeです
が、
this.props.html.element
に、<span class="style">htmlです</span>
が格納されます。
import React from 'react';
class Component extends React.Component {
render() {
return (
<section>
{this.props.html.element}
</section>
);
}
}
this.props.html
に格納されるHTMLはReactElementの配列に変換されているので、renderメソッド内で{this.props.html.element}
のように展開することができます。
サーバサイドから出力した値やDom構造をpropsに受け止めて使いまわすと色々と楽なので是非活用してみてください。
最後に
プルリクやissue、お待ちしております。
土日くらいしか直せないんですけど出来るだけ頑張りますよ。
あと@cognitomさんの「3分でできるnpmモジュール」を読んで初めてnpmを自作してみたのですが、とても楽しかったです。
npm installで自分の作ったモジュールが落ちてくる爽快感、たまりませんよ。
皆さんも是非挑戦してみてはいかがでしょうか。