118

My goal is to add components dynamically on a page/parent component.

I started with some basic example template like this:

main.js:

var App = require('./App.js');
var SampleComponent = require('./SampleComponent.js');
ReactDOM.render(<App/>, document.body);
ReactDOM.render(<SampleComponent name="SomeName"/>, document.getElementById('myId'));

App.js:

var App = React.createClass({
    render: function() {
        return (
            <div>
                <h1>App main component! </h1>
                <div id="myId">myId div</div>
            </div>

        );
    }

});

SampleComponent.js:

var SampleComponent = React.createClass({
    render: function() {
        return (
            <div>
                <h1>Sample Component! </h1>
            </div>
        );
    }
});

Here SampleComponent is mounted to <div id="myId"></div> node, which is pre-written in App.js template. But what if I need to add indefinite number of components to App component? Obviously I cannot have all the required divs sitting there.

After reading some tutorials I still have no understanding of how components are created and added to parent component dynamically. What is a way of doing it?

Boussadjra Brahim
  • 82,684
  • 19
  • 144
  • 164
Justin Trevein
  • 1,496
  • 3
  • 12
  • 16
  • 2
    Is there a reason why both your components mount to different elements? 99% of React apps only call `ReactDOM.render` once and all other components are children of the 'root' node – azium Apr 15 '16 at 15:55
  • 1
    No, I now understand this is not correct way :) – Justin Trevein Apr 15 '16 at 23:12

4 Answers4

90

You need to pass your components as children, like this:

var App = require('./App.js');
var SampleComponent = require('./SampleComponent.js');
ReactDOM.render(
    <App>
        <SampleComponent name="SomeName"/> 
    <App>, 
    document.body
);

And then append them in the component's body:

var App = React.createClass({
    render: function() {
        return (
            <div>
                <h1>App main component! </h1>
                {
                    this.props.children
                }
            </div>
        );
    }
});

You don't need to manually manipulate HTML code, React will do that for you. If you want to add some child components, you just need to change props or state it depends. For example:

var App = React.createClass({

    getInitialState: function(){
        return [
            {id:1,name:"Some Name"}
        ]
    },

    addChild: function() {
        // State change will cause component re-render
        this.setState(this.state.concat([
            {id:2,name:"Another Name"}
        ]))
    }

    render: function() {
        return (
            <div>
                <h1>App main component! </h1>
                <button onClick={this.addChild}>Add component</button>
                {
                    this.state.map((item) => (
                        <SampleComponent key={item.id} name={item.name}/>
                    ))
                }
            </div>
        );
    }

});
Andry
  • 16,172
  • 27
  • 138
  • 246
Nikolai Mavrenkov
  • 1,851
  • 18
  • 21
  • 2
    Thanks! I missed the fact that components can be defined in render() based on their state. Your answer here is the most complete one (mentions populating state with components) and answers the question exactly :) – Justin Trevein Apr 15 '16 at 23:17
  • How do you pass props to the children that have been appended? – BrightIntelDusk Dec 11 '17 at 23:29
  • 1
    So to follow up on this: if I want to make a single page app that has several tabs and popup windows from buttons, I need to go ahead and render those components (empty), hide them (somehow) and then modify state to make them "appear" at the right times? If you were to try to make a multi-tabbed single page app in React is that the best way to think of it? Or am I missing something? Thank you! – Elisabeth Mar 23 '18 at 19:11
  • 1
    @Elisabeth Yes, thats right. You actually have two main options: either always render everything and just hide things using CSS, conditionally applying CSS classes or inline styles; or render things conditionally, returning React element or null depending on your app state. These two approaches have pros and cons and often used both in the same app for different components depending on your needs. – Nikolai Mavrenkov Mar 23 '18 at 23:03
  • 1
    Thank you Nikolai! That really helps to solidify things in my mind about React. It's a very different way to design apps and think about the DOM than I'm used with plain JavaScript, and jQuery. – Elisabeth Mar 25 '18 at 01:32
  • Doesn't work for me. Complains about JS within return not valid React.js. Looks like it should work though. – user3335999 Mar 29 '19 at 21:40
9

Sharing my solution here, based on Chris' answer. Hope it can help others.

I needed to dynamically append child elements into my JSX, but in a simpler way than conditional checks in my return statement. I want to show a loader in the case that the child elements aren't ready yet. Here it is:

export class Settings extends React.PureComponent {
  render() {
    const loading = (<div>I'm Loading</div>);
    let content = [];
    let pushMessages = null;
    let emailMessages = null;

    if (this.props.pushPreferences) {
       pushMessages = (<div>Push Content Here</div>);
    }
    if (this.props.emailPreferences) {
      emailMessages = (<div>Email Content Here</div>);
    }

    // Push the components in the order I want
    if (emailMessages) content.push(emailMessages);
    if (pushMessages) content.push(pushMessages);

    return (
      <div>
        {content.length ? content : loading}
      </div>
    )
}

Now, I do realize I could also just put {pushMessages} and {emailMessages} directly in my return() below, but assuming I had even more conditional content, my return() would just look cluttered.

8

First, I wouldn't use document.body. Instead add an empty container:

index.html:

<html>
    <head></head>
    <body>
        <div id="app"></div>
    </body>
</html>

Then opt to only render your <App /> element:

main.js:

var App = require('./App.js');
ReactDOM.render(<App />, document.getElementById('app'));

Within App.js you can import your other components and ignore your DOM render code completely:

App.js:

var SampleComponent = require('./SampleComponent.js');

var App = React.createClass({
    render: function() {
        return (
            <div>
                <h1>App main component!</h1>
                <SampleComponent name="SomeName" />
            </div>
        );
    }
});

SampleComponent.js:

var SampleComponent = React.createClass({
    render: function() {
        return (
            <div>
                <h1>Sample Component!</h1>
            </div>
        );
    }
});

Then you can programmatically interact with any number of components by importing them into the necessary component files using require.

Neil Twist
  • 1,099
  • 9
  • 12
Chris
  • 1,101
  • 3
  • 17
  • 33
6

Firstly a warning: you should never tinker with DOM that is managed by React, which you are doing by calling ReactDOM.render(<SampleComponent ... />);

With React, you should use SampleComponent directly in the main App.

var App = require('./App.js');
var SampleComponent = require('./SampleComponent.js');
ReactDOM.render(<App/>, document.body);

The content of your Component is irrelevant, but it should be used like this:

var App = React.createClass({
    render: function() {
        return (
            <div>
                <h1>App main component! </h1>
                <SampleComponent name="SomeName"/>
            </div>
        );
    }
});

You can then extend your app component to use a list.

var App = React.createClass({
    render: function() {
        var componentList = [
            <SampleComponent name="SomeName1"/>,
            <SampleComponent name="SomeName2"/>
        ]; // Change this to get the list from props or state
        return (
            <div>
                <h1>App main component! </h1>
                {componentList}
            </div>
        );
    }
});

I would really recommend that you look at the React documentation then follow the "Get Started" instructions. The time you spend on that will pay off later.

https://facebook.github.io/react/index.html

Neil Twist
  • 1,099
  • 9
  • 12
  • How the array will populate the child compoenent without mapping? – solanki... Dec 24 '16 at 21:45
  • 1
    @solanki... A mapping is only required when the list is not a list of React components. The list here is of two `SampleComponent`s, which therefore doesn't require a mapping. If the list was a list of names, then it would require a mapping to React components. – Neil Twist Dec 28 '16 at 09:37
  • @solanki... Remember that the output of the mapping is simply another list – Neil Twist Dec 28 '16 at 09:38
  • 1
    Exactly what I was looking for. My use case was needing to optionally push JSX components into an empty `
    ` assigned to a variable that was then inserted into JSX as `{content}` in my return statement. Using an empty array was so easy!
    –  Feb 01 '17 at 08:11