43
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-03-15

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

43
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?