GreasemonkeyやChrome Extensionsなんかで既存ページを書き換えるアプリを作っているとき、
ページの複数箇所にDOMを挿入したいことがある。
これをReactでやりたい。
<body>
<nav>
<div>
<div>
<!-- ここに挿入したい -->
</div>
</div>
</nav>
<article>
<div>
<div>
<!-- ここにも挿入したい -->
</div>
</div>
</article>
</body>
ReactDOM.render
を二回やってもできるが、Reactツリー間での連携が面倒。
// 現仕様ではコンテナ内の既存要素は削除されるため、要コンテナ生成
const subRoot1 = document.createElement('div');
const subRoot2 = document.createElement('div');
document.querySelector('.container.of.subtree1').appendChild(subRoot1);
document.querySelector('.container.of.subtree2').appendChild(subRoot2);
ReactDOM.render(<SubTree1 {...dataForSubTree1} />, subRoot1);
ReactDOM.render(<SubTree2 {...dataForSubTree2} />, subRoot2);
やっぱり全体のルートが欲しい。
ルートはレンダリングせず、サブツリーを好きなとこにレンダリングするReactコンポーネントを作れないか?
...
で、やってみたら簡単にできた。Reactすごい!
以下ES6版の例。他でも似たようにできるはず。
class MainTree extends React.Component {
constructor(props, state) {
super(props, state);
this.subRoot1 = document.createElement('div');
this.subRoot2 = document.createElement('div');
document.querySelector('.container.of.subtree1').appendChild(this.subRoot1);
document.querySelector('.container.of.subtree2').appendChild(this.subRoot2);
this.state = { /* ... */ };
}
render() {
return null; // renderしない
}
componentDidMount() {
// マウント時にサブツリーをrenderする
ReactDOM.render(<SubTree1 {...this.state} />, this.subRoot1);
ReactDOM.render(<SubTree2 {...this.state} />, this.subRoot2);
}
componentDidUpdate() {
// 更新時にはサブツリーも更新
ReactDOM.render(<SubTree1 {...this.state} />, this.subRoot1);
ReactDOM.render(<SubTree2 {...this.state} />, this.subRoot2);
}
componentWillUnmount() {
// アンマウント時はサブツリー除去
ReactDOM.unmountComponentAtNode(this.subRoot1);
ReactDOM.unmountComponentAtNode(this.subRoot2);
}
componentWillReceiveProps(nextProps) {
this.setState({ /* ... */ })
}
}
const mainRoot = document.createElement('div');
ReactDOM.render(<MainTree {...dataForMainTree} />, mainRoot);
これが、
- マウント時にサブツリーをrenderしてOK
- ちなみに
render()
内でReactDOM.render
したら、componentDidUpdate
でやれと警告される
- ちなみに
-
ReactDOM.render
は複数回実行してもいい感じに更新してくれる - ルートのコンテナdivは文書内に挿入しなくても動いてくれる (警告出ないし別にいいよね?)
というわけでうまく動く。
これで実際のツリー構造に依存せず自由自在にReact使える。便利!