Help us understand the problem. What is going on with this article?

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

tkrkt
趣味エンジニアです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした