Agregue dinámicamente componentes secundarios en React

86

Mi objetivo es agregar componentes dinámicamente en una página / componente principal.

Comencé con una plantilla de ejemplo básica como esta:

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

Aquí SampleComponentestá montado en el <div id="myId"></div>nodo, que está escrito previamente en la App.jsplantilla. Pero, ¿qué sucede si necesito agregar un número indefinido de componentes al componente de la aplicación? Obviamente, no puedo tener todos los divs requeridos allí.

Después de leer algunos tutoriales, todavía no entiendo cómo se crean y agregan los componentes al componente principal de forma dinámica. ¿Cuál es una forma de hacerlo?

Justin Trevein
fuente
2
¿Hay alguna razón por la que ambos componentes se montan en elementos diferentes? 99% de las aplicaciones React solo llaman ReactDOM.renderuna vez y todos los demás componentes son hijos del nodo 'raíz'
azium
1
No, ahora entiendo que esta no es la forma correcta :)
Justin Trevein

Respuestas:

67

Debes pasar tus componentes como hijos, así:

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

Y luego añádalos en el cuerpo del componente:

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

No necesita manipular manualmente el código HTML, React lo hará por usted. Si desea agregar algunos componentes secundarios, solo necesita cambiar los accesorios o indicar que depende. Por ejemplo:

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
fuente
2
¡Gracias! Me perdí el hecho de que los componentes se pueden definir en render () según su estado. Su respuesta aquí es la más completa (menciona el estado
poblado
¿Cómo se pasan los accesorios a los niños que se han agregado?
BrightIntelDusk
Entonces, para dar seguimiento a esto: si quiero crear una aplicación de una sola página que tenga varias pestañas y ventanas emergentes de botones, necesito seguir adelante y renderizar esos componentes (vacíos), ocultarlos (de alguna manera) y luego modificar el estado para hacer "aparecen" en el momento adecuado? Si intentara crear una aplicación de una sola página con múltiples pestañas en React, ¿esa es la mejor manera de pensar en ello? ¿O me estoy perdiendo algo? ¡Gracias!
Elisabeth
1
@Elisabeth Sí, es cierto. En realidad, tiene dos opciones principales: renderizar siempre todo y ocultar las cosas usando CSS, aplicando condicionalmente clases CSS o estilos en línea; o renderizar cosas de forma condicional, devolviendo el elemento React o nulo según el estado de su aplicación. Estos dos enfoques tienen pros y contras y, a menudo, se utilizan ambos en la misma aplicación para diferentes componentes según sus necesidades.
Nikolai Mavrenkov
1
¡Gracias Nikolai! Eso realmente ayuda a solidificar las cosas en mi mente sobre React. Es una forma muy diferente de diseñar aplicaciones y pensar en el DOM de lo que suelo usar con JavaScript simple y jQuery.
Elisabeth
6

Primero, no usaría document.body . En su lugar, agregue un contenedor vacío:

index.html:

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

Entonces opte por renderizar solo su <App />elemento:

main.js:

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

Dentro de App.jsusted puede importar sus otros componentes e ignorar su código de renderizado DOM por completo:

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

Luego, puede interactuar mediante programación con cualquier número de componentes importándolos en los archivos de componentes necesarios usando require.

Chris
fuente
No hay problema, pude ver a alguien copiarlo, pegarlo y luego pasar horas en él ...
Neil Twist
6

Comparto mi solución aquí, basada en la respuesta de Chris. Espero que pueda ayudar a otros.

Necesitaba agregar dinámicamente elementos secundarios a mi JSX, pero de una manera más simple que las verificaciones condicionales en mi declaración de devolución. Quiero mostrar un cargador en el caso de que los elementos secundarios aún no estén listos. Aquí está:

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

Ahora, me doy cuenta de que también podría poner {pushMessages}y {emailMessages}directamente en mi parte return()inferior, pero suponiendo que tuviera aún más contenido condicional, mi return()se vería desordenado.


fuente
4

En primer lugar, una advertencia: nunca debe jugar con DOM administrado por React, lo que está haciendo llamando ReactDOM.render(<SampleComponent ... />);

Con React, debe usar SampleComponent directamente en la aplicación principal.

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

El contenido de su componente es irrelevante, pero debe usarse así:

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

Luego, puede extender el componente de su aplicación para usar una lista.

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

Realmente recomendaría que mire la documentación de React y luego siga las instrucciones de "Comenzar". El tiempo que le dedique se verá recompensado más tarde.

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

Neil Twist
fuente
¿Cómo poblará la matriz el componente secundario sin mapeo?
solanki ...
1
@solanki ... Solo se requiere un mapeo cuando la lista no es una lista de componentes de React. La lista aquí es de dos SampleComponents, por lo que no requiere un mapeo. Si la lista fuera una lista de nombres, entonces requeriría una asignación a los componentes de React.
Neil Twist
@solanki ... Recuerde que la salida del mapeo es simplemente otra lista
Neil Twist
1
Exactamente lo que estaba buscando. Mi caso de uso necesitaba empujar opcionalmente componentes JSX en un vacío <div>asignado a una variable que luego se insertó en JSX como {content}en mi declaración de devolución. ¡Usar una matriz vacía fue tan fácil!