Fügen Sie in React dynamisch untergeordnete Komponenten hinzu

86

Mein Ziel ist es, Komponenten dynamisch zu einer Seite / übergeordneten Komponente hinzuzufügen.

Ich habe mit einer grundlegenden Beispielvorlage wie dieser begonnen:

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

Hier SampleComponentwird an den <div id="myId"></div>Knoten angehängt , der in der App.jsVorlage vorab geschrieben ist . Aber was ist, wenn ich der App-Komponente eine unbestimmte Anzahl von Komponenten hinzufügen muss? Natürlich kann ich nicht alle erforderlichen Divs dort haben.

Nachdem ich einige Tutorials gelesen habe, habe ich immer noch kein Verständnis dafür, wie Komponenten erstellt und dynamisch zur übergeordneten Komponente hinzugefügt werden. Wie geht das?

Justin Trevein
quelle
2
Gibt es einen Grund, warum beide Komponenten an unterschiedlichen Elementen montiert werden? 99% der React-Apps rufen nur ReactDOM.rendereinmal auf und alle anderen Komponenten sind
untergeordnete Elemente
1
Nein, ich verstehe jetzt, dass dies nicht richtig ist :)
Justin Trevein

Antworten:

67

Sie müssen Ihre Komponenten als Kinder wie folgt übergeben:

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

Und fügen Sie sie dann im Körper der Komponente hinzu:

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

Sie müssen den HTML-Code nicht manuell bearbeiten. React erledigt dies für Sie. Wenn Sie einige untergeordnete Komponenten hinzufügen möchten, müssen Sie nur die Requisiten ändern oder angeben, dass dies davon abhängt. Zum Beispiel:

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

});
Nikolai Mavrenkov
quelle
2
Vielen Dank! Ich habe die Tatsache übersehen, dass Komponenten in render () basierend auf ihrem Status definiert werden können. Ihre Antwort hier ist die vollständigste (erwähnt den Bestückungszustand mit Komponenten) und beantwortet die Frage genau :)
Justin Trevein
Wie geben Sie Requisiten an die angehängten Kinder weiter?
BrightIntelDusk
Um dem nachzugehen: Wenn ich eine App mit nur einer Seite erstellen möchte, die mehrere Registerkarten und Popup-Fenster über Schaltflächen enthält, muss ich diese Komponenten rendern (leer), sie (irgendwie) ausblenden und dann den Status ändern sie "erscheinen" zum richtigen Zeitpunkt? Wenn Sie versuchen sollten, in React eine App mit mehreren Registerkarten für eine einzelne Seite zu erstellen, ist dies der beste Weg, um darüber nachzudenken? Oder fehlt mir etwas? Vielen Dank!
Elisabeth
1
@ Elizabeth Ja, das ist richtig. Sie haben tatsächlich zwei Hauptoptionen: Entweder immer alles rendern und Dinge einfach mit CSS ausblenden, CSS-Klassen oder Inline-Stile bedingt anwenden; oder rendern Sie Dinge bedingt, indem Sie je nach App-Status das React-Element oder null zurückgeben. Diese beiden Ansätze haben Vor- und Nachteile und werden je nach Ihren Anforderungen häufig in derselben App für unterschiedliche Komponenten verwendet.
Nikolai Mavrenkov
1
Danke Nikolai! Das hilft mir wirklich, die Dinge in meinem Kopf über React zu festigen. Es ist eine ganz andere Art, Apps zu entwerfen und über das DOM nachzudenken, als ich es mit einfachem JavaScript und jQuery verwende.
Elisabeth
6

Erstens würde ich document.body nicht verwenden . Fügen Sie stattdessen einen leeren Container hinzu:

index.html:

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

Dann entscheiden Sie sich, nur Ihr <App />Element zu rendern :

main.js:

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

Innerhalb können App.jsSie Ihre anderen Komponenten importieren und Ihren DOM-Rendercode vollständig ignorieren:

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

Anschließend können Sie programmgesteuert mit einer beliebigen Anzahl von Komponenten interagieren, indem Sie diese mithilfe in die erforderlichen Komponentendateien importieren require.

Chris
quelle
Kein Problem, ich konnte nur jemanden sehen, der es kopierte und dann Stunden damit verbrachte ...
Neil Twist
6

Teilen Sie meine Lösung hier, basierend auf Chris 'Antwort. Hoffe es kann anderen helfen.

Ich musste untergeordnete Elemente dynamisch an meine JSX anhängen, aber auf einfachere Weise als bedingte Überprüfungen in meiner return-Anweisung. Ich möchte einen Loader für den Fall zeigen, dass die untergeordneten Elemente noch nicht fertig sind. Hier ist es:

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

Jetzt ist mir klar, dass ich es auch einfach {pushMessages}und {emailMessages}direkt in mein return()unten stehendes return()Bild einfügen könnte , aber wenn ich noch mehr bedingten Inhalt hätte, würde mein Aussehen nur überladen aussehen.


quelle
4

Erstens eine Warnung: Sie sollten niemals an DOM basteln, das von React verwaltet wird, was Sie durch einen Anruf tun ReactDOM.render(<SampleComponent ... />);

Mit React sollten Sie SampleComponent direkt in der Haupt-App verwenden.

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

Der Inhalt Ihrer Komponente ist irrelevant, sollte jedoch folgendermaßen verwendet werden:

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

Sie können dann Ihre App-Komponente erweitern, um eine Liste zu verwenden.

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

Ich würde wirklich empfehlen, dass Sie sich die React-Dokumentation ansehen und dann den Anweisungen "Erste Schritte" folgen. Die Zeit, die Sie dafür aufwenden, zahlt sich später aus.

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

Neil Twist
quelle
Wie füllt das Array die untergeordnete Komponente ohne Zuordnung?
Solanki ...
1
@solanki ... Eine Zuordnung ist nur erforderlich, wenn die Liste keine Liste der React-Komponenten ist. Die Liste hier besteht aus zwei SampleComponents, für die daher keine Zuordnung erforderlich ist. Wenn die Liste eine Liste von Namen wäre, wäre eine Zuordnung zu React-Komponenten erforderlich.
Neil Twist
@ Solanki ... Denken Sie daran, dass die Ausgabe des Mappings einfach eine andere Liste ist
Neil Twist
1
Genau das, wonach ich gesucht habe. Mein Anwendungsfall bestand darin, JSX-Komponenten optional in ein Leerzeichen <div>zu verschieben, das einer Variablen zugewiesen war, die dann wie {content}in meiner return-Anweisung in JSX eingefügt wurde . Die Verwendung eines leeren Arrays war so einfach!