3

Is it possible to have a component with child components that will be mounted to different DOM objects?

Some pseudo-code to explain what I wanna archieve:

import ChildComponent1 from './Components/ChildComponent1';
import ChildComponent2 from './Components/ChildComponent2';

var MyParentComponent = React.createClass({
  render: function() {
    <ChildComponent1 />,
    document.querySelector('#component-1')

    <ChildComponent2 />,
    document.querySelector('#component-2')
  }
});

ReactDOM.render(
  <MyParentComponent />,
  //Inherit DOM mounts
);

This might not be the right way to handle it though - I'm open for suggestions. I want to do like this, to make MyParentComponent take care of the state values etc. If I just add multiple ReactDOM.render() I won't have any parent component.

Qar
  • 1,746
  • 3
  • 17
  • 24

4 Answers4

2

You can achieve this using the componentDidMount and componentDidUpdate hooks to manually render your child components when your parent component updates:

class Child1 extends React.Component {
  render() {
    const {count, onClick} = this.props;
    return (
      <div onClick={onClick}>Child 1 {count}</div>
    );
  }
}

class Child2 extends React.Component {
  render() {
    const {count, onClick} = this.props;
    return (
      <div onClick={onClick}>Child 2 {count}</div>
    );
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.container1El = document.querySelector("#container-1");
    this.container2El = document.querySelector("#container-2");
    this.handleClick = this.handleClick.bind(this);
    this.state = {count: 0};
  }

  handleClick() {
    this.setState({count: this.state.count + 1});
  }

  renderChildren() {
    const {count} = this.state;
    const {handleClick, container1El, container2El} = this;
    ReactDOM.render(<Child1 count={count} onClick={handleClick}/>, container1El);
    ReactDOM.render(<Child2 count={count} onClick={handleClick}/>, container2El);
  }

  componentDidMount() {
    this.renderChildren();
  }
  
  componentDidUpdate() {
    this.renderChildren();
  }
  
  componentWillUnmount() {
    ReactDOM.unmountComponentAtNode(this.container1El);
    ReactDOM.unmountComponentAtNode(this.container2El);
  }

  render() {
    return null;
  }
}

ReactDOM.render(<App/>, document.createElement("div"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="container-1"></div>
<div id="container-2"></div>
SimpleJ
  • 13,812
  • 13
  • 53
  • 93
  • Just don't forget to call `unmountComponentAtNode()` for both of them in parent's `componentWillUnmountI()`. Otherwise you're leaking them. – Dan Abramov Mar 11 '17 at 23:04
  • @DanAbramov is there a way to pass new props to child components rendered this way? – clhenrick May 16 '17 at 17:12
  • The componentWillMount(), componentShouldMount(), etc., funcs, no longer run in ReactJS 16.4.0 : https://github.com/facebook/react/blob/master/CHANGELOG.md#breaking-changes – HoldOffHunger Jun 04 '18 at 19:34
2

What you are looking for is a portal.

Sometimes it is useful to render a child element to a different dom tree than the natural one. For example you may want the child to get appended to body, generally for z-index or opacity reasons.

I've described it here: https://stackoverflow.com/a/31563614/82609

Consider the following:

<div className="a">
    a content
    <Portal target="body">
        <div className="b">
            b content
        </div>
    </Portal>
</div>

Could produce the following DOM when rendered inside reactAppContainer:

<body>
    <div id="reactAppContainer">
        <div className="a">
             a content
        </div>
    </div>
    <div className="b">
         b content
    </div>
</body>

Popular portal libraries are:

  • react-portal, useful for simple portals like popups/full screen layers.
  • react-tether, useful for "positioned portals" (ie relative to their parent) like tooltips, dropdowns, hotspots...
Community
  • 1
  • 1
Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
0

No need to create a parent component, you can use ReactDOM.render() multiple times

import ChildComponent1 from './Components/ChildComponent1';
import ChildComponent2 from './Components/ChildComponent2';


ReactDOM.render(
  <ChildComponent1 />,
  document.getElementById('component-1')
);

ReactDOM.render(
  <ChildComponent2 />,
  document.getElementById('component-2')
);

UPDATE

In order to have multiple components in one you can have your structure as:

import ChildComponent1 from './Components/ChildComponent1';
import ChildComponent2 from './Components/ChildComponent2';

var MyParentComponent = React.createClass({
  render: function() {
  return(
   <div>
    <ChildComponent1 />
    <div>Some text</div>
    <div>Some more text</div>
    <ChildComponent2 />
   </div>
  )}
});

ReactDOM.render(
  <MyParentComponent />,
  document.getElementById('app');
);
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 1
    Yes, but then I don't have a parent component to take care of states and props that needs to be available in all children components? :) – Qar Jul 25 '16 at 15:51
  • @Mithril you can't have a parent component and render the children to different dom nodes. That is not how react works. – erichardson30 Jul 25 '16 at 15:52
  • @erichardson30 So I would have to add the entire form as one huge react component, even though I only need to add functionality to a minor portion of it? :) – Qar Jul 25 '16 at 15:54
  • @Mithril As I said you can render multiple components to different divs but not children of a parent to different divs. And isn't my answer equivalent to what you want. Or you need something else – Shubham Khatri Jul 25 '16 at 16:00
  • @Mithril What you can do it is that you can create small components and then return a single component from a parent component that you can render. You can create as many divs as possible within a component as long as they all are enclosed within a single div that is returned – Shubham Khatri Jul 25 '16 at 16:03
  • @ShubhamKhatri but that will not let me have any other html in between these component, right? Then it will be a parent element with the react components only – Qar Jul 25 '16 at 16:06
  • @Mithril You can have any HTML between components as long as it returns a single div – Shubham Khatri Jul 25 '16 at 16:21
  • @ShubhamKhatri but the HTML has to be inside the javascript. A large form in JSX will just make the js code not so readable/navigable. – r0dney Jul 25 '16 at 16:24
  • @r0dney I am suggesting a method to what the OP wants. It upto OP to decide how to implement it. – Shubham Khatri Jul 25 '16 at 16:31
-1

The answer is no, it's not possible. This is the best you can do if you still want to use React for that.

var MyParentComponent = React.createClass({
  render: function() {
    <div>
      {this.props.children}
      <ChildComponent1 />
      <ChildComponent2 />
    </div>
  }
});

ReactDOM.render(
  <MyParentComponent />,
  //Inherit DOM mounts
);
r0dney
  • 735
  • 2
  • 5
  • 16
  • how would this make children mount to different DOM objects? – nuway Jul 25 '16 at 15:39
  • Because I have a big form, only a few fields should have any react functionality. So the react components should be mounted into different places in this big form. To do like you suggest, I will have to add the entire form as one big react component, which seems to be overkill to me. – Qar Jul 25 '16 at 15:40
  • I don't think you can have two React components mounted to two DOM nodes and have them under one root component. The only way I can think of is to call ReactDOM.render() twice with different child component, but they won't share a root component that way. – r0dney Jul 25 '16 at 15:44
  • 1
    @Mithril you cannot do that with react. all of the React functionality has to be mounted to one parent container – nuway Jul 25 '16 at 15:46
  • So, if I have a form with 100 input fields and I only need react functionality on 2 of these, I still need to add the entire form as one huge react component? – Qar Jul 25 '16 at 15:52
  • @Mithril And you only want to manage two with React and they have to be under one parent component? No, I think you are better off with two separate `ReactDOM.render()` and manage two separately. – r0dney Jul 25 '16 at 15:55
  • @r0dney but can you share states between two child components that are rendered separately? – Qar Jul 25 '16 at 15:57
  • @Mithril you can have one store(global variable perhaps) and both components read/write data to it if they need to interact with each other. – r0dney Jul 25 '16 at 16:00
  • @r0dney Ahh, do you have any example of how to archieve this? Is it just a vanilla js store/global var or is it something react related? – Qar Jul 25 '16 at 16:04
  • @Mithril personally I use react-redux all the time so for me it's just connecting two components to the same store. A plain global var works just the same, you can read your state from that variable and whenever you setState just update the global store. – r0dney Jul 25 '16 at 16:21