Динамічно додавати дочірні компоненти в 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один раз, а всі інші компоненти є дочірніми елементами кореневого вузла
azium

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
@Elisabeth Так, це правильно. Насправді у вас є два основні варіанти: або завжди все рендерити, і просто приховувати речі за допомогою CSS, умовно застосовуючи класи CSS або вбудовані стилі; або відтворювати речі умовно, повертаючи елемент React або null, залежно від стану вашого додатка. Ці два підходи мають плюси і мінуси і часто використовуються в одному додатку для різних компонентів залежно від ваших потреб.
Микола Мавренков 23.03.18

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. Список тут складається з двох SampleComponent, які, отже, не потребують відображення. Якби список був списком імен, тоді він вимагав би зіставлення компонентів React.
Ніл Твіст

@solanki ... Пам'ятайте, що результат відображення - це просто інший список
Ніл Твіст

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