在React中动态添加子组件


86

我的目标是在页面/父组件上动态添加组件。

我从这样的一些基本示例模板开始:

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>
        );
    }
});

此处SampleComponent已安装到<div id="myId"></div>预写在App.js模板中的节点上。但是,如果我需要向App组件中添加不确定数量的组件,该怎么办?显然,我不能将所有必需的div都坐在那里。

在阅读了一些教程之后,我仍然不了解如何动态创建组件并将其动态添加到父组件。有什么办法呢?


2
您的两个组件都安装到不同的元件上,是否有原因?99%的React应用程序仅调用ReactDOM.render一次,并且所有其他组件都是“根”节点的子级
azium

1
不,我现在知道这是不正确的方法:)
贾斯汀·特雷文

Answers:


67

您需要以子代形式传递组件,如下所示:

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

然后将它们附加到组件的主体中:

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

您不需要手动操作HTML代码,React会为您完成。如果要添加一些子组件,则只需更改道具或声明其依赖。例如:

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>
        );
    }

});

2
谢谢!我错过了一个事实,即可以根据组件的状态在render()中定义组件。您的答案是最完整的答案(用组件填充状态),并准确回答了问题:)
贾斯汀·特雷文

您如何将道具传递给已被追加的孩子?
BrightIntelDusk

因此,请继续进行以下操作:如果我想制作一个具有多个选项卡和按钮弹出窗口的单页面应用程序,则需要继续渲染这些组件(空),隐藏它们(以某种方式),然后修改状态以使其他们在正确的时间“出现”?如果您想在React中制作一个多选项卡的单页面应用程序,那是想到它的最佳方法吗?还是我错过了什么?谢谢!
伊丽莎白

1
@Elisabeth是的,没错。实际上,您有两个主要选择:要么始终呈现所有内容,要么仅使用CSS隐藏内容,有条件地应用CSS类或内联样式;或有条件地渲染事物,根据您的应用状态返回React元素或null。这两种方法各有利弊,并且经常根据需要在同一个应用程序中同时使用不同的组件。
Nikolai Mavrenkov '18

1
谢谢尼古拉!这确实有助于巩固我关于React的想法。与我使用普通JavaScript和jQuery相比,这是一种设计应用程序和考虑DOM的非常不同的方式。
伊丽莎白

6

首先,我不会使用document.body。而是添加一个空容器:

index.html:

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

然后选择仅渲染您的<App />元素:

main.js:

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

App.js您可以在其中导入其他组件,而完全忽略DOM渲染代码:

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>
        );
    }
});

然后,您可以使用,将它们导入必要的组件文件中,从而以编程方式与任意数量的组件进行交互require


没问题,我可以看到有人复制粘贴并将其花上几个小时……
Neil Twist

6

根据克里斯的回答,在这里分享我的解决方案。希望它可以帮助别人。

我需要将子元素动态地附加到JSX中,但是要比return语句中的条件检查更简单。在子元素尚未准备好的情况下,我想显示一个加载器。这里是:

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>
    )
}

现在,我不知道我也可以干脆把{pushMessages}{emailMessages}我的直接return()下方,但假设我有更多的条件内容,我return()只想显得杂乱。


4

首先警告:您永远不要修改由React管理的DOM,这是通过调用来实现的 ReactDOM.render(<SampleComponent ... />);

使用React,您应该直接在主应用程序中使用SampleComponent。

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

组件的内容无关紧要,但是应该这样使用:

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

然后,您可以扩展您的应用程序组件以使用列表。

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>
        );
    }
});

我真的建议您查看React文档,然后按照“入门”说明进行操作。您花在上面的时间将在以后得到回报。

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


数组如何在不映射的情况下填充子组件?
solanki ... 16/12/24

1
@solanki ...仅当列表不是React组件列表时才需要映射。此处的列表为2 SampleComponent,因此不需要映射。如果列表是名称列表,则需要映射到React组件。
Neil Twist

@solanki ...请记住,映射的输出只是另一个列表
Neil Twist

1
正是我想要的。我的用例是需要有选择地将JSX组件压入一个空<div>值,该空值分配给一个变量,然后像{content}我的return语句一样将其插入JSX 。使用空数组是如此简单!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.