Динамически добавлять дочерние компоненты в 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шаблоне. Но что, если мне нужно добавить неопределенное количество компонентов в компонент приложения? Очевидно, у меня не может быть всех необходимых div .

После прочтения некоторых руководств я все еще не понимаю, как компоненты создаются и добавляются в родительский компонент динамически. Как это сделать?


2
Есть ли причина, по которой оба ваших компонента устанавливаются на разные элементы? 99% приложений React вызывают только ReactDOM.renderодин раз, а все остальные компоненты являются
дочерними

1
Нет, теперь я понимаю, что это неправильный путь :)
Джастин Тревейн

Ответы:


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
@ Элизабет Да, именно так. На самом деле у вас есть два основных варианта: либо всегда отображать все, либо просто скрывать элементы с помощью CSS, условно применяя классы CSS или встроенные стили; или отображать вещи условно, возвращая элемент React или null в зависимости от состояния вашего приложения. У этих двух подходов есть свои плюсы и минусы, и они часто используются в одном приложении для разных компонентов в зависимости от ваших потребностей.
Николай Мавренков

1
Спасибо, Николай! Это действительно помогает укрепить мои представления о React. Это совсем другой способ разрабатывать приложения и думать о DOM, чем я использую простой JavaScript и jQuery.
Элизабет

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.


Нет проблем, я мог просто увидеть, как кто-то копирует его, а затем тратит на это часы ...
Нил Твист

6

Поделюсь своим решением здесь, основываясь на ответе Криса. Надеюсь, это поможет другим.

Мне нужно было динамически добавлять дочерние элементы в мой JSX, но более простым способом, чем условные проверки в моем операторе возврата. Я хочу показать загрузчик в случае, если дочерние элементы еще не готовы. Вот:

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

Во-первых, предупреждение: вы никогда не должны возиться с DOM, управляемым React, что вы делаете, вызывая 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 ...

1
@solanki ... Отображение требуется только тогда, когда список не является списком компонентов React. Список здесь состоит из двух SampleComponents, поэтому отображение не требуется. Если бы список был списком имен, то ему потребовалось бы сопоставление с компонентами React.
Нил Твист

@solanki ... Помните, что вывод сопоставления - это просто еще один список
Нил Твист

1
Именно то, что я искал. В моем варианте использования необходимо было дополнительно вставить компоненты JSX в пустой объект, <div>назначенный переменной, которая затем была вставлена ​​в JSX, как {content}в моем операторе возврата. Использовать пустой массив было так просто!
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.