Adicione dinamicamente componentes filhos no React

86

Meu objetivo é adicionar componentes dinamicamente em um componente de página / pai.

Comecei com um modelo básico de exemplo como este:

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

Aqui SampleComponentestá montado no <div id="myId"></div>nó, que é pré-escrito no App.jsmodelo. Mas e se eu precisar adicionar um número indefinido de componentes ao componente do aplicativo? Obviamente, não posso ter todos os divs exigidos ali.

Depois de ler alguns tutoriais, ainda não entendi como os componentes são criados e adicionados ao componente pai dinamicamente. Qual é a maneira de fazer isso?

Justin Trevein
fonte
2
Existe uma razão pela qual ambos os componentes são montados em elementos diferentes? 99% dos aplicativos React chamam apenas ReactDOM.renderuma vez e todos os outros componentes são filhos do nó 'raiz'
azium
1
Não, agora eu entendo que não é a maneira correta :)
Justin Trevein

Respostas:

67

Você precisa passar seus componentes como filhos, assim:

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

E, em seguida, anexe-os ao corpo do componente:

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

Você não precisa manipular manualmente o código HTML, o React fará isso por você. Se você quiser adicionar alguns componentes filhos, você só precisa alterar os adereços ou definir que depende. Por exemplo:

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
fonte
2
Obrigado! Perdi o fato de que os componentes podem ser definidos em render () com base em seu estado. Sua resposta aqui é a mais completa (menciona o estado de preenchimento com componentes) e responde exatamente à pergunta :)
Justin Trevein
Como você passa acessórios para as crianças que foram anexadas?
BrightIntelDusk
Então, para dar continuidade a isso: se eu quiser fazer um aplicativo de página única que tenha várias guias e janelas pop-up de botões, preciso ir em frente e renderizar esses componentes (vazios), ocultá-los (de alguma forma) e, em seguida, modificar o estado para fazer eles "aparecem" nos momentos certos? Se você fosse tentar fazer um aplicativo de página única com várias guias no React, é a melhor maneira de pensar nisso? Ou eu estou esquecendo de alguma coisa? Obrigado!
Elisabeth
1
@Elisabeth Sim, isso mesmo. Na verdade, você tem duas opções principais: sempre renderizar tudo e apenas ocultar as coisas usando CSS, aplicando classes CSS ou estilos embutidos condicionalmente; ou renderizar as coisas condicionalmente, retornando o elemento React ou null dependendo do estado do seu aplicativo. Essas duas abordagens têm prós e contras e costumam ser usadas no mesmo aplicativo para componentes diferentes, dependendo de suas necessidades.
Nikolai Mavrenkov
1
Obrigado Nikolai! Isso realmente ajuda a solidificar as coisas em minha mente sobre o React. É uma maneira muito diferente de projetar aplicativos e pensar sobre o DOM do que a que estou acostumada com JavaScript simples e jQuery.
Elisabeth
6

Primeiro, eu não usaria document.body . Em vez disso, adicione um contêiner vazio:

index.html:

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

Em seguida, opte por renderizar apenas o seu <App />elemento:

main.js:

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

Dentro, App.jsvocê pode importar seus outros componentes e ignorar completamente o código de renderização 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>
        );
    }
});

Em seguida, você pode interagir programaticamente com qualquer número de componentes, importando-os para os arquivos de componentes necessários usando require.

Chris
fonte
Não tem problema, eu pude ver alguém copiando e colando e depois passando horas nele ...
Neil Twist
6

Compartilhando minha solução aqui, com base na resposta de Chris. Espero que possa ajudar outras pessoas.

Eu precisava anexar dinamicamente elementos filho em meu JSX, mas de uma maneira mais simples do que verificações condicionais em minha instrução de retorno. Quero mostrar um carregador no caso de os elementos filhos ainda não estarem prontos. Aqui 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>
    )
}

Agora, eu percebo que também poderia colocar {pushMessages}e {emailMessages}diretamente no meu return()abaixo, mas supondo que eu tivesse ainda mais conteúdo condicional, o meu return()pareceria desordenado.


fonte
4

Em primeiro lugar, um aviso: você nunca deve mexer no DOM gerenciado pelo React, o que você está fazendo chamando ReactDOM.render(<SampleComponent ... />);

Com o React, você deve usar SampleComponent diretamente no aplicativo principal.

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

O conteúdo do seu componente é irrelevante, mas deve ser usado assim:

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

Você pode então estender o componente do seu aplicativo para usar uma 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>
        );
    }
});

Eu realmente recomendo que você leia a documentação do React e siga as instruções de "Primeiros passos". O tempo que você gasta nisso terá retorno mais tarde.

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

Neil Twist
fonte
Como a matriz preencherá o componente filho sem mapeamento?
solanki ...
1
@solanki ... Um mapeamento só é necessário quando a lista não é uma lista de componentes React. A lista aqui é de dois SampleComponents, o que, portanto, não requer um mapeamento. Se a lista fosse uma lista de nomes, seria necessário um mapeamento para os componentes do React.
Neil Twist
@solanki ... Lembre-se de que a saída do mapeamento é simplesmente outra lista
Neil Twist
1
Exatamente o que eu estava procurando. Meu caso de uso foi a necessidade de empurrar opcionalmente componentes JSX em um vazio <div>atribuído a uma variável que foi então inserida em JSX como {content}em minha instrução de retorno. Usar um array vazio foi tão fácil!