JavaScript
reactjs
React

ReactでサブツリーからDOMをレンダリングする

More than 3 years have passed since last update.

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使える。便利!