Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
39
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@tkrkt

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

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

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
39
Help us understand the problem. What are the problem?