Aggiungi dinamicamente componenti figlio in React


89

Il mio obiettivo è aggiungere componenti dinamicamente su una pagina / componente principale.

Ho iniziato con un modello di esempio di base come questo:

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

Qui SampleComponentè montato su <div id="myId"></div>node, che è pre-scritto nel App.jstemplate. Ma cosa succede se devo aggiungere un numero indefinito di componenti al componente App? Ovviamente non posso avere tutti i div richiesti seduti lì.

Dopo aver letto alcuni tutorial, non ho ancora capito come i componenti vengono creati e aggiunti dinamicamente al componente padre. Qual è un modo per farlo?


2
C'è un motivo per cui entrambi i componenti si montano su elementi diversi? Il 99% delle app React chiama solo ReactDOM.renderuna volta e tutti gli altri componenti sono figli del nodo 'root'
AZIUM

1
No, ora capisco che questo non è il modo corretto :)
Justin Trevein

Risposte:


70

Devi passare i tuoi componenti da bambini, in questo modo:

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

E poi aggiungili nel corpo del componente:

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

Non è necessario manipolare manualmente il codice HTML, React lo farà per te. Se vuoi aggiungere alcuni componenti figlio, devi solo cambiare gli oggetti di scena o dichiarare che dipende. Per esempio:

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
Grazie! Mi è sfuggito il fatto che i componenti possono essere definiti in render () in base al loro stato. La tua risposta qui è la più completa (menziona lo stato di popolamento con componenti) e risponde esattamente alla domanda :)
Justin Trevein

Come si passano gli oggetti di scena ai bambini che sono stati aggiunti?
BrightIntelDusk

1
Quindi, per proseguire su questo: se voglio creare un'app a pagina singola con diverse schede e finestre popup dai pulsanti, devo andare avanti e rendere quei componenti (vuoti), nasconderli (in qualche modo) e quindi modificare lo stato per renderlo li "compaiono" al momento giusto? Se dovessi provare a creare un'app a pagina singola con più schede in React, è questo il modo migliore per pensarla? Oppure mi sfugge qualcosa? Grazie!
Elisabeth

1
@Elisabeth Sì, è vero. In realtà hai due opzioni principali: o renderizza sempre tutto e nascondi le cose usando CSS, applicando condizionatamente classi CSS o stili inline; o eseguire il rendering delle cose in modo condizionale, restituendo l'elemento React o null a seconda dello stato dell'app. Questi due approcci hanno vantaggi e svantaggi e spesso vengono utilizzati entrambi nella stessa app per componenti diversi a seconda delle esigenze.
Nikolai Mavrenkov

1
Grazie Nikolai! Questo aiuta davvero a consolidare le cose nella mia mente su React. È un modo molto diverso di progettare app e pensare al DOM rispetto a quello che uso con JavaScript e jQuery.
Elisabeth

7

Primo, non userei document.body . Aggiungi invece un contenitore vuoto:

index.html:

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

Quindi scegli di rendere solo il tuo <App />elemento:

main.js:

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

All'interno App.jspuoi importare gli altri componenti e ignorare completamente il codice di rendering 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>
        );
    }
});

Quindi è possibile interagire a livello di codice con un numero qualsiasi di componenti importandoli nei file dei componenti necessari utilizzando require.


Nessun problema, ho potuto vedere qualcuno che lo copia-incolla e poi ci passa ore sopra ...
Neil Twist

6

Condivido la mia soluzione qui, sulla base della risposta di Chris. Spero che possa aiutare gli altri.

Avevo bisogno di aggiungere dinamicamente elementi figlio nel mio JSX, ma in un modo più semplice rispetto ai controlli condizionali nella mia dichiarazione di ritorno. Voglio mostrare un caricatore nel caso in cui gli elementi figlio non siano ancora pronti. Ecco qui:

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

Ora, mi rendo conto che potrei anche mettere {pushMessages}e {emailMessages}direttamente nel mio return()sotto, ma supponendo che avessi ancora più contenuti condizionali, il mio return()apparirebbe disordinato.


4

Innanzitutto un avvertimento: non dovresti mai armeggiare con DOM gestito da React, cosa che stai facendo chiamando ReactDOM.render(<SampleComponent ... />);

Con React, dovresti usare SampleComponent direttamente nell'App principale.

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

Il contenuto del tuo componente è irrilevante, ma dovrebbe essere usato in questo modo:

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

È quindi possibile estendere il componente dell'app per utilizzare un elenco.

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

Ti consiglio davvero di guardare la documentazione di React e poi di seguire le istruzioni "Per iniziare". Il tempo che dedichi a questo ti ripagherà in seguito.

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


In che modo l'array popolerà il componente figlio senza mappatura?
solanki ...

1
@solanki ... Una mappatura è richiesta solo quando l'elenco non è un elenco di componenti React. L'elenco qui è di due SampleComponents, che quindi non richiede una mappatura. Se l'elenco fosse un elenco di nomi, richiederebbe una mappatura ai componenti React.
Neil Twist

@solanki ... Ricorda che l'output della mappatura è semplicemente un altro elenco
Neil Twist

1
Esattamente quello che stavo cercando. Il mio caso d'uso era la necessità di spingere facoltativamente i componenti JSX in un vuoto <div>assegnato a una variabile che è stata quindi inserita in JSX come {content}nella mia dichiarazione di ritorno. Usare un array vuoto è stato così facile!
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.