React: Adicione a API do fragmento para permitir o retorno de vários componentes da renderização

Criado em 2 set. 2014  ·  148Comentários  ·  Fonte: facebook/react


Nota dos mantenedores:

Sabemos que isso é um problema e sabemos exatamente qual conjunto de problemas pode ser resolvido. Queremos isso também, mas é um _problema difícil_ com nossa arquitetura atual. Comentários adicionais expressando desejo por esse recurso não são úteis. Sinta-se à vontade para se inscrever na edição (há um botão na coluna da direita), mas não comente a menos que esteja agregando valor à discussão. "Eu também" e "+1" não são valiosos, nem casos de uso que já foram escritos nos comentários (por exemplo, sabemos que você não pode colocar elementos <tr> ou <dd> com <div> ).


Considere o seguinte:

var ManagePost = React.createClass({

  render: function() {
    var posts = this.props.posts

    var something;
    var somethingelse;

    var row = posts.map(function(post){
      return(
        <div>
          <div className="col-md-8">
          </div>
          <div className="cold-md-4">
          </div>
        </div>
      )
    });

    return (
        {row}
    );
  }

});

Se você remover o <div></div> no map , você obterá o seguinte erro: _Elementos XJS adjacentes devem ser envolvidos em uma tag envolvente_

não é até que eu adicione novamente as divs ao redor, e bastante inúteis, que ele compila sem problemas. Estou executando 0.11.1

Isso está sendo abordado? Ele adiciona extra e novamente - IMO - html inútil e sem sentido à página, que, embora não prejudique nada - parece confuso e pouco profissional. Talvez eu esteja apenas fazendo algo errado, por favor me esclareça se eu estiver.

Comentários muito úteis

Acho que podemos fechar isso.

O retorno de arrays de componentes é suportado desde o React 16 Beta 1, que você pode experimentar agora .

Ainda existem algumas limitações (o suporte SSR não está pronto), mas estamos rastreando-as em #8854 e corrigimos antes da versão 16 final.

Obrigado a todos pelo feedback!

Todos 148 comentários

Porque quando você não coloca o wrapper, ele desuga para isso:

return React.DOM.div(...)React.DOM.div(...)

O que não faz sentido sintático. A página do compilador jsx pode ajudar se você precisar de um mapeamento visual.

Dito isto, é possível reduzir o açúcar para [div, div] . Isso é difícil, um tanto controverso e não será implementado em um futuro próximo.

(Não acho que seja particularmente controverso, mas adiciona complexidade ao código e ainda não foi feito.)

IIRC @syranide tem alguns comentários sobre isso

@chenglou Hehe.

Eu tive uma breve conversa sobre isso com chenglou há pouco tempo, eu realmente não me inclino para um lado ou para o outro no momento. Vejo muitos perigos ocultos em permitir que um componente composto retorne vários componentes e isso quebra muitas suposições intuitivas, mas não conheço nenhum caso de uso de boas práticas (no momento) que obviamente se beneficiaria disso.

A simplicidade de retornar no máximo um componente significa que é muito fácil raciocinar sobre o que você vê, se não, <table><TableHeader /></table> poderia realmente renderizar qualquer número de linhas, você não tem como saber além de inspecionar TableHeader e quaisquer componentes compostos que ele retornar.

Eu sinto que ser capaz de retornar vários componentes apenas move a responsabilidade de encapsular componentes conforme necessário do "componente composto" para o que "renderiza o componente composto". O componente "que renderiza o componente composto" raramente tem ou deveria saber se deve ou não encapsular componentes compostos, enquanto as crianças são mais propensas a conhecer seus pais.

Mas talvez seja simplesmente um caso de responsabilidade do desenvolvedor. Pode haver bons casos de uso para ambos e devemos apenas olhar além do inevitável mau uso.

@sebmarkbage Provavelmente tem alguns comentários também :)

Provavelmente nunca permitiremos essa sintaxe implicitamente. Você precisaria de um invólucro como

<>
  <div className="col-md-8">
  </div>
  <div className="cold-md-4">
  </div>
</>

OU

[
  <div className="col-md-8">
  </div>,
  <div className="cold-md-4">
  </div>
]

No entanto, mesmo isso não funciona. Muitas vezes, é provavelmente o melhor. Pode ser confuso consumir componentes quando um filho pode se expandir em vários elementos.

Mas, na verdade, a única razão pela qual não apoiamos isso agora é porque é difícil de implementar. Espero que possamos apoiá-lo em algum momento no futuro, mas provavelmente não a curto prazo. Desculpe. :/

isso não pode afetar coisas como jquery ou outras bibliotecas que visam elementos específicos, então se você fizer algo como $('#id-name').children() , o seguinte:

<div id="id-name">
  <div>
    <div class="class-name">
    </div>
  </div>
</div>

o <div> e <div class="class-name"> seriam selecionados neste caso. (Se eu entendi bem)

Isso também afeta os seletores css da mesma maneira que o @AdamKyle postou antes.

Alguma atualização para esse problema?

Passei alguns minutos entendendo por que meu componente não funcionou. Eu sinto que deveria haver um aviso em algum lugar, talvez eu tenha perdido? Talvez seja obviamente errado tentar:

var Optimistic = React.createClass({
  render: function() {
    return ( 
      <h1>{this.props.name} loves React</h1>
      <p>React doesn’t. Idea: sprinkle some divs here and there.</p>
    );
  }
});

React.render(
  <Optimistic name="Peter" />,
  document.getElementById('myContainer')
);

@gabssnake Você deve ter recebido um erro de compilação JSX com o erro "Elementos XJS adjacentes devem ser empacotados em uma tag envolvente"; você não viu o erro ou não ficou claro em sua explicação?

Obrigado pela sua resposta @spicyj. Bem, eu quis dizer um aviso na documentação do React. Sim, o console mostrou um erro, mas a necessidade de embrulhar não fazia sentido para mim no início. Por isso pesquisei e cheguei aqui.

Eu também tive essa dor... particularmente dolorosa para o meu designer, na verdade. Seria bom se um componente pudesse gerar um nó (portanto, lista de nós ou fragmento) em vez de um elemento.

Apenas dizendo .. Eu não estou defendendo o retorno de vários filhos do componente _mas_ eu adoraria fazer isso nos métodos render* que eu extraio de render :

  render: function () {
    return (
      <div className={this.getClassName()}
           style={{
             color: this.props.color,
             backgroundColor: this.props.backgroundColor
           }}>
        {condition ?
          this.renderSomething() :
          this.renderOtherThing()
        }
      </div>
    );
  },

  renderSomething() {
    return (
      <>
        <div className='AboutSection-header'>
          <h1>{this.props.title}</h1>
          {this.props.subtitle &&
            <h4>{this.props.subtitle}</h4>
          }
        </div>,

        {hasChildren &&
          <div className='AboutSection-extra'>
            {this.props.children}
          </div>
        }
      </>
    );
  }

Mas eu provavelmente deveria calar a boca e usar key s.

@gaearon Você já pode fazer isso, você só precisa retornar um array por enquanto (o que é um pouco complicado, sim) ... mas, você pode contornar isso, eu hackeei meu próprio componente <Frag> que é traduzido para um array (sobrecarregado React.render ) ... você também pode fazer return <NoopComp>...</NoopComp>.props.children eu suponho, se você quiser evitar hacks.

EDIT: Foi mal, eu sobrecarreguei React.createElement não React.render .

O problema com arrays é que eles enganam nosso designer. Precisa de vírgulas, chaves explícitas.

@gaearon Sim, você pode evitar as vírgulas usando qualquer uma das minhas duas soluções alternativas por enquanto (se você achar aceitável) ... mas o que você quer dizer com chaves explícitas?

Se eu usar a sintaxe de matriz, preciso especificar key em cada elemento. Não que seja difícil de fazer, mas parece estranho porque eu sei que eles nunca mudam.

@gaearon Ah sim, eu escolho apenas ignorar mentalmente esse aviso por enquanto :), se você realmente quiser evitá-lo, você pode fazer <MyComp children={this.renderWhatever()} /> para evitá-lo ( EDIT: embora você obviamente não possa usar isso se você tiver filhos adjacentes, você pode usar algum ajudante de achatamento ... mas sim).

Outro caso que encontrei com um kit de interface do usuário. Quando você está colocando filhos dentro de um container rolável fixo assim:

return (
  <div style={{
    position: fixed
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow-y: scroll;
  }}>
    {this.props.children}
  </div>
);

Os filhos agora devem ser passados ​​como um array, mas se uma classe composta for passada, ela também deve implementar esses estilos para evitar quebrá-la. Apenas adiciona uma camada de complexidade. Mas eu posso entender completamente o quão complexo deve ser fazer a mudança. Apenas jogando meu chapéu em apoio.

Eu tenho outro caso de uso que descrevi em detalhes aqui #3415. Mas posso contornar isso por enquanto e entender que é difícil de implementar e raro.

Eu não sei nada sobre react internals (ainda), mas vou apenas dar uma ideia de como você pode marcar esses elementos/fragmentos pais virtuais no DOM: comentários. Por exemplo

<A>
    <B></B>
    <Fragment>
        <C></C>
        <D></D>
    </Fragment>
    <E></E>
</A>

renderia a

<a>
    <b></b>
    <!--<fragment data-reactid="">-->
        <c></c>
        <d></d>
    <!--</fragment>-->
    <e></e>
</a>

Isso significa que c e d serão tratados como filhos de sth. (incluindo o reactid aninhado para não entrar em conflito com b e e). Eu vi comentários de outros frameworks de "uso indevido" para esses tipos de trabalhos semânticos.

@Prinzhorn Acho que você pode estar confundindo a saída DOM com o JSX do React.

Caso seja útil: a sugestão do @Prinzhorn de usar comentários HTML para mapear "componentes de fragmentos" para o DOM é a mesma abordagem que o Knockout usa. Knockout chama esses "elementos virtuais".

<!-- ko component: "message-editor" -->
<!-- /ko -->

(de documentos de nocaute )

Além disso, outro caso de uso para isso - os elementos de encapsulamento extras podem ser problemáticos ao usar o flexbox.

@aldendaniels concordo absolutamente, já tive problemas com o flexbox.

Eu encontrei isso com flexbox com dropdowns dependentes. Onde A renderizaria e gerenciaria a primeira lista suspensa, seguida por B ou C ou D, dependendo do valor da lista suspensa de A, que renderia cada um o número apropriado de listas suspensas, por exemplo

<A>
 <Drop />
 <C><Drop /><Drop /></C>
</A>

A, B, C e D são sem estado, então eu os alterei de class B {render(){ this.props }} para function B(props){ return [...]; } .

Nesse caso, não importa muito para o pai que vários filhos sejam renderizados, é apenas para lidar com meu CSS. Há coisas que eu faria diferente se elas não precisassem ser reutilizadas (outro componente precisa de B, C e D).


Como alternativa, talvez uma maneira de desvendá-lo do pai? Eu não tenho nenhuma idéia específica de como isso ficaria.

Encontrei um cenário hoje que acho que é um bom caso de uso para esse recurso: Um componente que renderiza vários elementos <script> , onde poderia renderizar no elemento <head> da página. Qualquer elemento wrapper lá seria ruim.

Meu cenário está querendo ter um componente que seja responsável por renderizar a tag <script> tanto para o código de tempo de execução necessário na página quanto outra tag <script> que carrega as strings localizadas a serem usadas pelo o código de tempo de execução. Por exemplo:

<html>
    <head>
        <script language="runtime.resources.en-us.js"></script>
        <script language="runtime.js"></script>
    </head>
    <body>
    ...
    </body>
</html>

Nesse caso, gostaria de ter o código criado como:

var RuntimeScripts = require('./Runtime')
...
return (
    <html>
        <head>
            <RuntimeScripts language="en-us" />
        </head>
    </html>
)
...

Eu também encontrei alguns problemas de flexbox. Nada que não possa ser resolvido em CSS, mas uma das "belezas" do flexbox é que você precisa de menos elementos "wrapper" em todos os lugares para fazer seu layout funcionar, mas ainda acabará com elementos wrapper em todos os lugares ao usar React desde você sempre envolve o que retorna em div/div ou similar, a menos que faça sentido ter um contêiner.

Para todos os casos de uso apresentados aqui, tenho certeza que você poderia substituir <BunchOfComponents /> por {getBunchOfComponents()} e a saída visual seria a mesma, sem introduzir os problemas práticos e técnicos relacionados a ter componentes com fragmentos como raiz.

@syranide, mas toda vez que um dos componentes muda, todos os seus irmãos precisam ser recalculados ...

Além disso, se você usar coffeescript simples, é fácil retornar uma matriz, portanto, dissocie a funcionalidade da representação jsx.
IOW se for fácil lidar com uma matriz de elementos retornada, não espere que o jsx alcance.

@syranide, mas toda vez que um dos componentes muda, todos os seus irmãos precisam ser recalculados ...

@wmertens Sim, mas muitas vezes você teria isso de qualquer maneira porque o pai precisaria rerenderizar por outros motivos ou simplesmente porque você recebe os dados por meio de props de qualquer maneira. Mas sim, essa é a diferença, mas não significa que essa abordagem esteja correta, é uma otimização e há muitas maneiras de realizá-las.

Além disso, se você usar coffeescript simples, é fácil retornar uma matriz, portanto, dissocie a funcionalidade da representação jsx.

Isso é irrelevante e não é um problema com o JSX. Um grande problema é que você perde a suposição técnica, prática e intuitiva de um componente = um elemento/nó . Não posso falar pelos desenvolvedores, mas não desistiria disso de bom grado, é uma suposição muito útil de se ter. Tenho certeza de que há otimizações igualmente boas ou melhores que podem ser projetadas se a otimização for a única razão pela qual as pessoas querem isso.

@syranide o maior problema é que você nem sempre pode usar um embrulho
elemento em html, como em tabelas, listas, flexbox, head... Trabalhando ao redor
que leva a código feio.

Eu ficaria perfeitamente feliz com um elemento wrapper virtual que apenas renderiza
comentários, como sugerido anteriormente.

Em sex, 29 de maio de 2015, 15h56 Andreas Svensson [email protected]
escrevi:

@syranide https://github.com/syranide mas toda vez que um dos
componentes muda todos os seus irmãos precisam ser recalculados...

@wmertens https://github.com/wmertens Sim, mas muitas vezes você faria
ter isso de qualquer maneira porque o pai precisaria renderizar novamente para outros
motivos, ou simplesmente porque você recebe os dados através de adereços de qualquer maneira. Mas
sim, essa é a diferença, mas isso não significa que essa abordagem seja correta,
é uma otimização e há muitas maneiras de realizá-las.

Além disso, se você usar coffeescript simples, é fácil retornar uma matriz, então, por favor
desacoplar a funcionalidade da representação jsx.

Isso é irrelevante e não é um problema com o JSX. Uma grande questão é que
você perde o pressuposto técnico, prático e intuitivo de _one
componente = um elemento/nó_. Eu não posso falar pelos devs, mas eu não faria
desista disso de bom grado, é uma suposição muito útil de se ter. Tenho certeza
existem otimizações igualmente boas ou melhores que poderiam ser projetadas se
otimização é a única razão pela qual as pessoas querem isso.


Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/facebook/react/issues/2127#issuecomment -106810565.

fwiw, é relativamente fácil hackear um componente "fragment" no React que é tratado como um array de seus filhos pelo React. Ele gerará chaves automaticamente, mas como isso acontece após a validação inicial dos componentes, não lançará o erro usual "nenhuma chave fornecida".

Com isso dito, esse hack apenas resolve o que @gaearon estava falando acima - não ter que lidar com a sintaxe feia da matriz / definir chaves arbitrárias em seu JSX - e não o problema de retornar vários nós na raiz do método de renderização de um componente.

Eu tenho um problema com a ideia de que um componente precisa retornar um "elemento/nó". Para mim, parece perfeitamente razoável para uma estrutura JSX de:

<Main>
  <Foo />
  <Fragment>
    <Bar />
    <Baz />
  </Fragment>
</Main>

para acabar como o DOM:

<div>
  <div>Foo</div>
  <div>Bar</div>
  <div>Baz</div>
</div>

Eu não acho que isso seja uma violação do princípio da menor surpresa porque os componentes já fazem todos os tipos de "coisas surpreendentes" com o DOM usando ganchos de ciclo de vida (basta olhar para o padrão comum de buraco de minhoca). É geralmente aceito que um componente não resultará necessariamente na criação de um único elemento, e tudo bem, porque alguns compromissos precisam ser feitos para funcionar com o DOM.

Também não se trata de "otimizações", ou mesmo de não gostar de sintaxe de matriz. Como muitos usuários mencionaram, os _elementos wrapper quebram o estilo e o layout de maneira séria_. As tabelas são as mais óbvias, mas o Flexbox também é um problema importante. Eu já tenho CSS que apenas reaplica regras flex para elementos wrapper que só existem por causa do React, e é bem feio.

Para todos os casos de uso apresentados aqui, tenho certeza que você pode substituir com {getBunchOfComponents()} e a saída visual seria a mesma, sem introduzir as questões práticas e técnicas relacionadas a ter componentes com fragmentos como root.

Isso exige que os desenvolvedores se comprometam a fazer componentes isolados e reutilizáveis ​​- que Deus os ajude se eles decidirem que querem reutilizar seus componentes em outro lugar - por causa de um problema de implementação subjacente no React. Acho que isso não deve ser aceito.

@thomasboyt

EDIT: Meu erro, confundi alguns de seus argumentos com a discussão da tabela acima, concordo amplamente com o que você está dizendo, eu acho. Mas ainda há problemas com componentes sendo opacos, então o que se destina a ser um wrapper transparente útil torna-se opaco para o pai. Imagine <Wrapper1><Wrapper2>...</Wrapper2></Wrapper1> , Wrapper1 não pode ver os filhos de Wrapper2 . Então talvez wrapMyElements(...) simplesmente seja uma solução melhor ainda (incluindo qualquer outra funcionalidade de suporte necessária).

Eu tenho um problema com a ideia de que um componente precisa retornar um "elemento/nó". Para mim, parece perfeitamente razoável para uma estrutura JSX de:

Os componentes são mais do que apenas wrappers estúpidos, eles têm um propósito. IMHO parece que retornar vários elementos bloqueia algumas expectativas muito úteis. Por exemplo, React.render obterá um companheiro no futuro que renderizará um elemento e retornará os nós, isso agora deve produzir uma matriz de nós.

Mas acho que uma questão muito importante é a da legibilidade que IMHO é o maior ponto de venda do React, tudo é explícito.

<table>
  <tr>
    <td />
    <td />
    <td />
  </tr>
  <tr>
    <Columns1 />
    <Columns2 />
  </tr>
</table>

Olhando para isso não faz sentido, de onde vem a 3ª célula? Talvez esteja realmente errado e renderizando 2 ou 4 células, quem sabe, talvez seja realmente dinâmico e dependa de um prop ou estado externo? Existem muitas variações desse problema que só ficam mais difíceis quando você considera outros frontends não-HTMLDOM que podem ter expectativas explícitas. Outra coisa a considerar é que os elementos são opacos, então se você substituir <tr /> por <MyMagicalTr /> então ele não será capaz de interagir com as células individuais ou mesmo deduzir quantas existem, então mesmo que <MyMagicalTr /> pode aceitar apenas <MyMagicalTd /> 's, não há garantia de que possa realmente interagir com eles.

Isso exige que os desenvolvedores se comprometam a fazer componentes isolados e reutilizáveis ​​- que Deus os ajude se eles decidirem que querem reutilizar seus componentes em outro lugar - por causa de um problema de implementação subjacente no React. Acho que isso não deve ser aceito.

"Isso exige que os desenvolvedores se comprometam em tornar isolado ...", mas esse é exatamente o problema se você me perguntar, se um componente pode retornar vários elementos, ele não está mais isolado, é substituído, o componente está vazando para o pai.

Martelo, pregos. Sendo uma questão de implementação subjacente, é uma questão separada de se isso deve ou não ser feito. Não é minha decisão, mas não vejo como um caso de uso raro seja um argumento convincente sem considerar as compensações que o acompanham ou quais outras soluções alternativas existem.

IMHO Não vejo problema com {getBunchOfComponents()} , é explícito, nos permite manter nossas expectativas úteis. Se o desempenho for um problema, então React.createSmartFragment() (ou w/e) para o resgate, um tipo transparente de array/objeto, mas que pode ser atualizado independentemente de seu pai.

Novamente, os desenvolvedores do React são a autoridade (não eu), mas não vejo um argumento convincente aqui considerando os vários efeitos colaterais. Nem tenho certeza se concordo com a solução apresentada ser um bom padrão, mesmo que tenha sido suportada.

EDIT: Para esclarecer, talvez os componentes possam retornar vários elementos no futuro, porque existem outros casos de uso obviamente benéficos, especialmente no contexto de passagem por filhos (como o que você mostra a @thomasboyt), a legibilidade é mantida.

Acho que vou precisar de um pouco mais de café antes de poder responder ao lado filosófico desta conversa (obrigado pelos pontos muito bons, @syranide), mas no lado da implementação, comecei a bisbilhotar isso ontem à noite para ver como viável é uma mudança desse escopo, levando a este pico: https://github.com/facebook/react/compare/master...thomasboyt :fragment

E lançou uma pequena demonstração aqui: http://www.thomasboyt.com/react-fragment-demo/

Algumas observações sobre o lado da implementação das coisas:

  • Não surpreendentemente, é muito complicado adaptar um sistema que espera que "1 componente = 1 nó" suporte mais nós;)
  • Inicialmente, pensei em tentar rastrear fragmentos no lado das operações do DOM, para que as instruções de mutação geradas por ReactMultiChild pudessem permanecer as mesmas e tratar fragmentos como qualquer outro nó. No entanto, não consegui pensar em uma boa maneira de adicionar estado sobre contagens de nós/quais nós são fragmentos no rastreamento de estado do DOM. Algo como o comentário esgrima que @Prinzhorn observou poderia funcionar, mas desconfio de qualquer coisa que exija uma pesquisa de DOM, considerando o custo relativo.
  • Com essa ideia descartada, adicionei um campo _nodeCount a todos os filhos de um componente ReactMultiChild , para que ele pudesse rastrear o número de nós raiz que um fragmento realmente contém.

O problema é que, embora isso seja fácil de fazer em uma renderização inicial apenas contando os filhos de um fragmento, atualizar a contagem de nós do fragmento em mutações subsequentes parece mais complicado. Isso ainda não foi feito no meu branch (veja https://github.com/thomasboyt/react/issues/2).

  • Muitas operações DOM dependem do acesso ao nó pai de um elemento, pesquisado pelo ID do nó interno, para anexar/mover/remover elementos (consulte https://github.com/thomasboyt/react/issues/3). Como o ciclo updateComponent do ReactMultiChild é responsável por passar esse ID adiante, ele pode ser alterado para fazer uma pesquisa do pai mais próximo que tenha um nó DOM, mas isso parece caro. Alternativamente, pode ser possível ter um registro interno de chaves de fragmento para suas chaves de "nó real".

Ainda não estou convencido de que exigir que fragmentos mantenham uma contagem de seus nós raiz é a melhor maneira de fazer isso (embora pelo menos tenha me levado a essa demonstração), e tudo isso foi hackeado rapidamente e bem tarde da noite , então se alguém tiver uma sugestão de implementação, sinta-se à vontade para entrar em contato :>

@thomasboyt IIRC o principal obstáculo de implementação vem do React referenciando nós filhos por mountIndex , isso não funciona quando um "nó" pode de repente se tornar qualquer número de nós e isso pode acontecer sem invocar o pai e também pode acontecer vários componentes em profundidade (envolvimento). Se não me engano, é bastante trivial ter o React suportando vários elementos raiz, desde que o número nunca mude.

Portanto, não acho que seria especialmente difícil fazê-lo funcionar no React, mas uma solução verdadeiramente adequada é mais problemática e provavelmente deve envolver a eliminação mountIndex .

@syranide Certo; a solução em que estou trabalhando na verdade introduz um novo nodeIndex que deveria ser o "deslocamento real" de um nó (o que me lembra que preciso voltar e remover mountIndex , já que Eu acho que agora não é usado no meu ramo).

Mas, como você observa, isso é problemático se o número de elementos raiz mudar, pois o nodeIndex de um componente precisa ser atualizado sempre que a contagem de nós de um componente irmão anterior for alterada. Ainda precisa encontrar uma solução para isso.

Eu também tive problemas com o flexbox. @syranide , você poderia elaborar um pouco mais sobre a solução proposta "getBunchOfComponents"? Sendo novo no React, é difícil ter uma ideia completa de onde definir essa função / como aplicá-la.

@landabaso

function getBunchOfComponents(...) {
  return [<ColumnA key="a" />, <ColumnB key="b" />];
}

Ei,

Eu não li todo o tópico, mas aqui está um caso de uso de otimização de renderização que pode exigir esse recurso:

http://stackoverflow.com/questions/30976722/react-performance-rendering-big-list-with-purerendermixin

Se esse recurso for lançado, o ReactCSSTransitionGroup não precisará mais de um nó wrapper, certo?

@slorber Sim, isso provavelmente é verdade.

Corra para a necessidade desse recurso todos os dias.

Se você tem muitos componentes pequenos (ou seja, projeta muito modularmente), você acaba tendo que envolver todos os tipos de coisas em divs que não deveriam ser. Posso estar confundindo, mas acho que isso se relaciona com esse problema.

Para <div> 's você pode envolvê-los em um <div> , mas para linhas de tabela elementos <tr> , não é tão fácil. Você pode envolver <tr> em <tbody> , mas pode não ser desejável ter várias camadas de <tbody> envolvendo várias camadas de <tr> 's.

O cenário que mencionei estava tentando ter um componente que fornecesse os elementos <link> e <script> sem ter que se tornar inteiramente o renderizador <head> .

Eu adicionei uma nota ao topo desta questão. Por favor, leia antes de comentar. https://github.com/facebook/react/issues/2127#issue -41668009

Bump... eu tenho uma grande necessidade dele no lado do servidor. É muito complicado renderizar páginas web completas (excluindo doctype) sem a capacidade de renderizar fragmentos, por causa da seção <head> . Atualmente estou trabalhando nisso através de mixins e um pouco de lógica na renderização final, mas seria muito mais simples se houvesse suporte para renderização de vários componentes.

@impinball , você pode tentar escrever algo semelhante a react -document-title com base em react-side-effect para resolver esses problemas. Consegui fazer o mesmo para meta tags, cabeçalhos, título e ocasionalmente redirecionamentos

Estou enfrentando esse problema também, existem soluções alternativas por enquanto? Não consegui fazer {getBunchOfComponents()} funcionar como sugerido.

Nenhum outro além dos já mencionados.

@jonchay Você pode criar um componente que renderiza apenas seus filhos.

function statelessWrapper(props) {
   return props.children;
}

e depois para usá-lo:

render() {
   return (  
      <statelessWrapper>
         {renderABunchOfComponents()}
      </statelessWrapper>
    );
}

@whatknight Isso não funcionará, exceto nos casos em que return renderABunchOfComponents(); já funciona.

  render () {
    let user = this.state.user
    let profile = user.get('data')
    let view = null

    if (user.get('status') === 'fetched') {
      view = (
        <h1>{profile.get('login')}</h1>
        <img src={profile.get('avatar_url')} />
        <dl>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>
      )
    } else if (user.get('status') === 'fetching') {
      view = <h1>fetching</h1>
    } else if (user.get('status') === 'error') {
      view = <h1>{profile.message}</h1>
    }

    return (
      <div className={className}>
        {view}
      </div>
    )
  }

Deve haver pelo menos uma maneira de retornar vários fragmentos ao fazer interpolação e "montagem". O exemplo acima está reclamando que img e h1 são adiacentes, mas eles acabarão dentro do wrapper principal de qualquer maneira. Esse é um elemento wrapper do qual eu gostaria de me livrar.

@kilianc neste caso, você pode simplesmente escrever

      view = [
        <h1 key={0}>{profile.get('login')}</h1>,
        <img key={1} src={profile.get('avatar_url')} />,
        <dl key={2}>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>,
      ]

do jeito que você está usando, não fará diferença se esse problema for resolvido.

Eu preciso desse recurso por motivos já declarados, então tentei implementar um contêiner <frag></frag> em https://github.com/mwiencek/react/tree/frag-component

A implementação não é realmente bonita, mas se funcionar para as pessoas, posso enviar um PR e deixar os desenvolvedores do React destruí-lo.

@mwiencek Parece que sua implementação não funciona se o número de filhos em um fragmento for alterado em uma atualização (_nestedChildCount é definido apenas em mountComponent)? Há um pouco de dificuldade para fazer tudo isso funcionar bem. Parece que você tem um bom começo embora. Na verdade, tenho pensado nisso novamente recentemente e posso ter descoberto uma maneira robusta de fazer isso acontecer. Eu vou relatar de volta se eu encontrar sucesso.

@spicyj sim, você está certo, vou precisar analisar isso ...

Super feliz que possamos ver uma implementação adequada em breve, no entanto. :) Sinta-se à vontade para copiar os testes desse branch se eles forem de alguma utilidade.

@spicyj O caminho a seguir não é usar createFragment e fazer com que o JSX se transforme nisso? Ou realmente queremos que os fragmentos sejam elementos?

Para construir e expandir no último comentário de @syranide , parece não haver necessidade de uma "API de fragmento" extra se a renderização permitir matrizes como valor de retorno. O JSX pode transformar vários elementos raiz em uma matriz, o que também funcionaria para valores de retorno de qualquer outra função. Então, em vez de introduzir uma superfície de API adicional, que requer documentação e aprendizado, uma das limitações do React pode ser removida.

Isso afetaria pelo menos babel-plugin-transform-react-jsx (implementação) e também babel-plugin-syntax-jsx (remoção de erro de análise para elementos raiz adjacentes). Embora alterar o primeiro pareça ser bastante seguro, não sei o escopo / uso do último e o impacto que a alteração proposta teria em outros projetos.

Isso ainda não cobre o caso de uso de uma condicional com vários elementos. Eu não considero "Usar uma matriz e adicionar manualmente um key={...} arbitrário a cada elemento" como uma solução adequada de longo prazo.

concordo com @dantman

sim, bom ponto. A geração automática de chaves deve ser incorporada por meio da transformação. Usar o índice do array como chave deve ser suficiente, pois os itens não estão mudando.

Quanto às condicionais, isso também pode ser incorporado à transformação ou, alternativamente, você pode usar JSX-Control-Statements . Implementado lá desta forma, daí a ideia.

Para lidar com as atualizações corretamente, imaginei que a solução que o @spicyj pensou para #5753 também funcionaria para fragmentos (envolvendo o conteúdo em algo como <!-- react-frag: 1 --><!-- /react-frag: 1 --> ). Sim, os comentários são um pouco feios, mas é muito mais confiável do que eu estava tentando fazer com _nestedChildCount . Essa abordagem agora é usada em https://github.com/mwiencek/react/tree/frag-component

Eu não vi isso mencionado no tópico até agora, mas acho que resolver isso também melhora a composição. Por exemplo, imagine que você tenha uma grade na qual deseja que as células desapareçam em uma determinada ordem. Idealmente, haveria dois componentes em jogo aqui: um para lidar com o layout e outro para lidar com a animação. Você teria uma API assim:

<GridLayout
  columns = { 3 }
>
  <FadeAnimator
    springConfig = { springConfig }
  >
    { ...cells }
  </FadeAnimator>
</GridLayout>

Isso permitiria que você mudasse para um layout diferente, ou uma animação diferente, sem que um precisasse saber sobre os detalhes de implementação do outro. GridLayout espera receber uma lista de crianças. FadeAnimator interceptaria essa lista, injetaria os estilos apropriados e/ou ouvintes de eventos e retornaria a nova lista para GridLayout consumir. Não há razão para FadeAnimator se preocupar com o layout de uma grade, exceto que os elementos React não podem retornar Arrays da renderização. Além disso, não há uma maneira simples de substituir a grade por, digamos, um layout de alvenaria, porque FadeAnimator está sendo solicitado a atuar como um contêiner para seus filhos.

Com as limitações atuais, suponho que você possa fazer algo assim:

<FadeAnimator
  wrapper = {
    <GridLayout
      columns = { 3 }
    />
  }
  springConfig = { springConfig }
>
  { ...cells }
</FadeAnimator>

// FadeAnimator
render() {
  return React.cloneElement(
    props.wrapper,
    null,
    props.children
  );
}

mas isso torna o código menos claro, mais complexo e mais difícil de compor.

Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!
Adicione a API do fragmento para permitir o retorno de vários componentes da renderização!

A sugestão da @texttechne é melhor. Em vez de introduzir API adicional, o react deve lidar com vários elementos raiz na renderização.

Manipular vários elementos raiz na renderização seria difícil, eu acho.
Isso significa que existe: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L1089

Você teria uma matriz de elementos em vez de um elemento.
Devido a isso, até onde eu entendo, você teria que instanciar vários elementos React: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js# L471

Em seguida, monte vários elementos React instanciados: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L471

E reconcilie toda a marcação produzida na boa ordem.

Eu acho que vem com desvantagens que podem não querer contornar o processo de reconciliação.
Queremos ter fragmentos como elementos ou fragmentos como sintaxe de açúcar em torno de transformações?

Acho que esse fragmento como elemento está bom, só precisaríamos criar um novo tipo de nó interno semelhante a nós de texto ou nós vazios, certo? Embora eu não saiba como nós os gerenciaríamos.

Por exemplo, como você lida quando uma das raízes é desmontada? Como você lida com a atualização corretamente?
Ou, como você gerencia múltiplas raízes dentro do DevTools? (resposta óbvia: corrija o DevTools...)

Eu acho que um fragmento é um componente composto. Onde está exatamente a diferença?
Se acabarmos duplicando o código para implementar fragmentos, é melhor implementarmos a sintaxe sugar para manter os internos do React "pristine" ?

Estou me perguntando, tenho brincado com os internos do React em torno da questão Subtree (renderSubtreeIntoContainer) e sinto que está um pouco relacionado. Quando você quiser renderizar em uma nova subárvore, você terá que renderizar uma nova raiz de fato. Então, se dermos suporte a várias raízes em um nível de árvore, renderizamos novas raízes a cada vez:

<p>Hi</p>
<p>There</p>

resultaria em duas chamadas "renderizar em uma nova raiz".

Em vez de uma chamada se usássemos um wrapper, certo? E quanto ao desempenho? Simplicidade? Para ser honesto, meu sentimento é: não devemos tocar nos internos do React para lidar com essa situação. Em vez disso, podemos realizar esse feito com o JSX? Podemos melhorar a sintaxe JSX?

(_Isenção de responsabilidade_: não estou totalmente acostumado com os internos do React, e pode haver algumas partes que não entendo completamente ou que não entendi. Minhas desculpas pelo mal-entendido.)

Edit: consertando/esclarecendo coisas. Além disso, o GitHub misteriosamente estiliza e-mails de forma estranha, então eu tive que reformatar o bloco de código... :-(

Oi, principal colaborador/confirmador do Mithril aqui.

TL;DR: Os fragmentos são extremamente difíceis, mesmo quando a API e os internos são
simples.

A propósito, sei por experiência que é _muito_ difícil de implementar. este
também foi solicitado várias vezes para o Mithril, mas recusou por causa
a pura dificuldade. Todas as tentativas de implementá-lo falharam com pelo menos
pelo menos um terço do conjunto de testes falhando.

Ainda estou trabalhando nos detalhes de uma biblioteca vdom que estou planejando escrever,
e ele tratará tudo como um fragmento, mas isso é algo que você tem
para literalmente (re)escrever a parte de renderização do zero. Como Reagir,
ele será desacoplado do DOM, mas a API será significativamente diferente
conceitualmente para renderização.

Aqui está o problema com os fragmentos: você precisa gerenciá-los completamente
internamente ou você não os diferencia adequadamente. Até
document.createContextualFragment é inútil. Apenas como exemplo, vamos
transformar duas árvores, renderizando omitido:

// Before
A {}
fragment {
  B[class="foo"] {}
  B[class="bar"] {}
}
C {}
D {}

// After
A {}
B[class="foo"] {}
fragment {
  C {}
}
D {}

A transformação correta para isso deve ser substituir os elementos B e o elemento C , deixando o resto intocado. Descobrir isso não é trivial, e você precisa basicamente iterar os filhos do fragmento enquanto ignora o fato de que eles estão em um fragmento.

Mas quando o fragmento acaba, você tem que lidar com a semântica do gancho, como
shouldComponentUpdate (não lembro o nome do React para esse gancho). assim
você ainda precisa rastrear os fragmentos de forma independente. Você difere seus
conteúdo como se fossem parte de seu fragmento pai, mas você ainda tem
para acompanhar a posição desse fragmento por causa do componente.

Ou em outras palavras, os componentes não estão mais intrinsecamente ligados aos seus
nó DOM. Em vez disso, eles são vinculados ao fragmento correspondente. React, como a maioria das outras bibliotecas e frameworks vdom, acopla intrinsecamente o componente à sua representação em árvore, mesmo com os tipos esperados. Esta é a maneira mais fácil de implementar um algoritmo diff que
manipula componentes. Quando eles são desacoplados, você tem que separar
escrituração de ambos. Você não inicializa componentes quando inicializa
o nó. Agora são dois processos completamente separados. É difícil fazer
inicialmente, e ainda mais difícil adicionar suporte para depois.

Obrigado a todos pelas palavras. Sabemos que isso é difícil e ainda temos isso em nossa lista de tarefas. (Perguntar com entusiasmo não fará com que aconteça mais cedo @janryWang.)

@isiahmeadows FYI, o branch de reescrita do Mithril suporta fragmentos.

@spicyj Você está convidado a dar uma olhada na implementação [1] [2] e testes [1] [2] se você ainda não estiver seguindo. Todo o mecanismo de diferença tem apenas cerca de 400 LOC, por isso deve ser fácil de seguir

@isiahmeadows Acho que o GitHub comeu parte do seu comentário. O bloco de código está quebrado e não consigo ver a primeira instância de <D /> passada.

Parece OK no e-mail embora. Talvez você tenha encontrado um bug no GitHub?

Infelizmente, o tratamento de markdown do GitHub se comporta de maneira diferente quando um comentário vem de um email. Editei o comentário para remover uma linha em branco e agora ele aparece.

Encaminhei o e-mail original para support@github. Felizmente, eles podem corrigir o analisador. 😃

@lhorie Você usa sintaxe de matriz para fragmento?
você tem polyfill para DocumentFragment?

E usar um elemento wrapper "pseudo", como um comentário HTML, não é uma opção? Eu pensei que era assim que os nós de texto eram "resolvidos" ...

Obrigado por corrigir esse comentário @spicyj

Para resolver as preocupações que @isiahmeadows levantou em seu exemplo: o novo mecanismo Mithril _não_ segue a semântica que @isiahmeadows sugeriu por vários motivos:

  • implementar essas semânticas tornaria a diferenciação _significativamente_ mais complexa
  • isso tornaria difícil raciocinar sobre chaves e bugs relacionados a chaves, pois torna-se possível que os espaços de chave sejam sangrados de componentes e até mesmo em componentes irmãos e subfilhos.
  • isso tornaria os ciclos de vida do fragmento não intuitivos (por exemplo, nesse exemplo, B.bar é removido, mas o fragmento também é, e um novo fragmento é criado para envolver C). Isso viola o princípio geral de que os ciclos de vida são "em cascata", o que significa que você não pode mais ter certeza de que um nó filho será removido se um determinado nó pai for removido. Como o ponto anterior, isso tem o potencial de causar vazamentos nos recursos de encapsulamento de um componente.
  • se, hipoteticamente, alguém encontrar problemas de comparação relacionados a um mecanismo de comparação que não adere a essas semânticas, a solução do espaço de aplicação é tão trivial quanto envolver um fragmento em torno do nó incorreto.

Eu estaria interessado se a equipe principal pudesse expandir a nota no topo re: _por que_ isso é difícil com a arquitetura atual. Vendo que o mecanismo de renderização do React e do Mithril está fundamentalmente tentando resolver os mesmos problemas, e que o Mithril agora suporta fragmentos em um grau que acredito ser viável e útil, talvez implementá-lo no React possa ser mais viável se diferentes aspectos da semântica são avaliados separadamente (e potencialmente rejeitados) como foi feito com o Mithril.

Note que corrigi meu comentário. Cometi alguns erros, e o GitHub não estiliza bem as respostas de e-mail... :frowning:

@Primajin Eu também me perguntei isso, mas suspeito que eles seriam passados ​​como um elemento. É importante tornar os fragmentos combináveis ​​(veja meu exemplo acima ). No entanto, provavelmente há momentos em que você gostaria de tratá-los como uma unidade também.

Talvez, React.Children.map deva expandir os fragmentos. Se você quiser iterar sobre todos os filhos (incluindo filhos de fragmentos filhos), use Children.map . Se você quiser tratar fragmentos como uma caixa opaca, trabalhe diretamente com props.children como um {array, element}.

@lhorie Eu não estive tão envolvido na reescrita, então não estou tão familiarizado com seus meandros. Estive ocupada com o fato de que tenho uma prova final esta semana e três na próxima, além de estar trabalhando com alguém para organizar um estágio que minha faculdade exige. Também estou focado em terminar o Techtonic, que _quase_ fiz o CLI (um teste está quebrado que não deveria estar).

@isiahmeadows Apenas um lembrete amigável para manter o tópico. Sinta-se à vontade para usar a sala de mithril se quiser conversar sobre outros tópicos.

@appsforartists

Talvez, React.Children.map deva expandir fragmentos

O Mithril stable faz algo semelhante internamente (ou seja, achatando sub-matrizes), mas estou me afastando desse modelo por motivos de perf (e também devido a algumas dores de cabeça históricas em relação a listas de vnode que misturavam nós com e sem chave). Pode ser algo a considerar.

E usar um elemento wrapper "pseudo", como um comentário HTML, não é uma opção? Eu pensei que era assim que os nós de texto eram "resolvidos" ...

Estamos usando https://github.com/mwiencek/react-packages em produção há alguns meses. Ele usa essa abordagem de wrapper de comentários, para que os fragmentos possam ser aninhados sem ambiguidade.

@mwiencek É possível usar sua abordagem sem o pacote de reação personalizado?

@mwiencek os invólucros de comentários são necessários? Eu não esperaria coisas inteligentes de fragmentos, se você mover um elemento de um fragmento para um fragmento irmão ou o elemento raiz, ele pode ser recriado.

Então, se você seguir a árvore vdom na ordem, não precisa de comentários, ou precisa?

De qualquer forma, sua solução parece ser exatamente o que é necessário para resolver esse problema, à primeira vista. 👍

Não estritamente, mas eles tornaram a implementação mais simples neste caso.

Então, essencialmente, atualmente não é possível criar listas de descrição apropriadas <dl> com React?

<dl>
  <dt>Def 1</dt>
  <dd>Some description</dd>
  <dt>Def 2</dt>
  <dd>Some other description</dd>
</dl>

@KaiStapel Este problema é sobre o retorno de vários componentes (ou elementos, eu acho) de render() . Contanto que sua função render retorne apenas um elemento / componente raiz, ela deve funcionar.

OK:

render() {
  return (
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

Não está tudo bem:

render() {
  return (
    <h2>my list</h2>
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

@GGAlanSmithee Bem codificado sim, mas você não pode fazer:

<dl>
   loop here and print out dt/dd pairs
</dl>

O que é muito triste. O mesmo vale para tabelas com rowspans, já que você não pode renderizar dois elementos <tr> de uma só vez :(

Do topo:

"Eu também" e "+1" não são valiosos, nem casos de uso que já foram escritos nos comentários (por exemplo, sabemos que você não pode colocar elementos <tr> ou <dd> com um <div>) .

Dado que existe uma solução de trabalho em https://github.com/mwiencek/react-packages , há alguma chance de que isso faça parte do React em breve? Ou estamos esperando o novo reconciliador?

Dado que existe uma solução de trabalho em https://github.com/mwiencek/react-packages

Você está usando com sucesso em projetos reais?

@mwiencek

A implementação não é realmente bonita, mas se funcionar para as pessoas, posso enviar um PR e deixar os desenvolvedores do React destruí-lo.

Claro, por favor envie um PR!

Sobre enviar um PR, a última vez que ouvi do @spicyj foi que eles queriam terminar o novo algoritmo principal e fazer uma solução adequada com isso, já que os nós de comentários não fazem sentido no React Native. Eu não tenho acompanhado o status disso, mas não acho que esses planos tenham mudado. Fico feliz que as pessoas achem os pacotes úteis nesse meio tempo.

O novo algoritmo está em andamento e suporta fragmentos. No entanto, eu não esperaria que ele ficasse pronto para produção nos próximos meses. Gostaria de saber se adicionar isso primeiro ao React DOM e depois ao React Native é muito ruim? A desvantagem é que fragmenta um pouco o ecossistema (trocadilho intencional!), mas pode nos dar algum tempo para experimentar esse recurso. Temos uma reunião de equipe hoje, então vou levantar essa questão se tivermos tempo para entender melhor.

@gaearon Posso apenas salientar que o suporte a fragmentos é super simples, é apenas açúcar, atualmente estou usando <frag> por meio de um pequeno wrapper trivial. Retornar vários filhos como a raiz do componente é realmente importante?

@syranide Estou usando a compilação personalizada com frag no ambiente beta, mas gostaria de usar a compilação oficial do React. Você pode fornecer seu wrapper <frag> ? :) Obrigado

@amertak

import React from 'react';
import createFragment from 'react-addons-create-fragment';

let nativeCreateElement = React.createElement;

React.createElement = function() {
  if (arguments[0] !== 'frag') {
    return nativeCreateElement.apply(this, arguments);
  }

  let length = arguments.length;
  if (length <= 2) {
    return null;
  }

  let children = {};
  for (let i = 2; i < length; i++) {
    children['~' + (i - 2)] = arguments[i];
  }

  return createFragment(children);
};

Conversamos mais sobre isso na última reunião da equipe. O consenso é que não queremos ir com essa implementação em particular. No entanto, esse recurso será suportado na reescrita principal de longo prazo (sem cronograma para isso por enquanto).

Também consideraremos isso novamente como uma das coisas em que poderíamos trabalhar no segundo semestre deste ano se a reescrita demorar muito ou não funcionar. Não há garantias de que ele fará parte da lista, mas manteremos todos vocês atualizados se surgir.

Para ter uma ideia melhor do que trabalhamos, consulte nosso repositório de notas de reunião! Você pode encontrar nossa discussão mais recente sobre isso em https://github.com/reactjs/core-notes/blob/master/2016-07/july-07.md.

@gaearon Seria interessante com tração ter pelo menos uma sintaxe de fragmentação oficial.

@syranide Obrigado pelo código, mas infelizmente parece que não posso usá-lo, pois preciso do frag como o componente raiz do aplicativo que está sendo renderizado pelo método ReactDOM.render e esse método não aceitará fragmento .

Obrigado de qualquer maneira, será útil para outras pessoas que não precisam do frag como root do aplicativo.

@amertak Sim, é apenas para habilitar uma sintaxe mais razoável para criar fragmentos, não adiciona novos recursos.

@syranide
Eu estava pensando Se seria possível renderizar comentário manualmente e tratar é como outro componente (que pode não ser necessário)?
Os comentários internos são tratados como #comment , então talvez seja possível chamar
React.createComponent('#comment', { ... }, children) ?
Apenas uma ideia. Pequena solução alternativa.

A principal coisa que falta aqui é poder renderizar o nó comment , estou certo? :)

@gaearon Um pouco triste que isso não aconteça em breve, mas agradeço a transparência. Boa escrita!

Uma solução alternativa até uma atualização?
Para mim, preciso renderizar um menu suspenso do Botstraap

render(){
    return (
        <ButtonNotification/>
        <ul className="dropdown-menu">
            {this.state.items.map(this.addItem)}
        </ul>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listNotification")
);

Mas não é possível, uma idéia?
Obrigado,

http://getbootstrap.com/components/#btn -dropdowns-single

Está claramente envolvido em um único <div class="btn-group"> conforme mostrado nos Documentos

Sim, claro, mas há dois elementos envolvidos em <div class="btn-group"> .

render(){
    return (
        <ButtonNotification/> //ELEMENT 1
        <ul className="dropdown-menu"> //ELEMENT 2
            {this.state.items.map(this.addItem)}
        </ul>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listNotification") //is DIV#listNotification
);
<div id="listNotification" class="btn-group"><!--wrap-->
    <a href="#">button notification</a> <!--ELEMENT1-->
    <ul> <!--ELEMENT2-->
        <li></li>
        <li></li>
    </ul>
</div>

E dois elementos envolvidos em um único elemento não são possíveis,
Obrigado pelo seu tempo @Primajin

Basta mudar tudo para um nível:

render(){
    return (
        <div className="btn-group"> //WRAP
            <ButtonNotification/> //ELEMENT 1
            <ul className="dropdown-menu"> //ELEMENT 2
                {this.state.items.map(this.addItem)}
            </ul>
        </div>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listWrapper") //or whatever your list is called
);

Ok, mas eu tenho outros itens no pai.
Isso só vai mover o problema.

<div ...> <--!listWrapper-->
    <div class="btn-group">....</>
    <div class="btn-group">....</>
    <--!React-->
    <div class="btn-group"> //WRAP
        <ButtonNotification/> //ELEMENT 1
        <ul className="dropdown-menu"> //ELEMENT 2
            {this.state.items.map(this.addItem)}
        </ul>
    </div>
    <--!React-->
    <div class="btn-group">....</>
</div>

E neste caso, o React substituirá todos os contidos.
É possível fazer um "add" sem substituir os outros elementos?

Obrigado,

@rifton007 não é assim que funciona. Você pode ter vários elementos/componentes como irmãos, agrupados em um contêiner. A restrição é que um componente não pode return vários elementos. O código que você acabou de postar funcionará.

Dito isto, se você apresentar um exemplo que não funciona, é educado ler o tópico inteiro e considerar se você está ou não adicionando um _novo_ exemplo ou apenas repetindo o mesmo problema que já foi reconhecido. Esta é uma restrição conhecida e este tópico contém uma discussão extensa dos detalhes e raciocínio para a restrição. Dan também afirmou que pretendem consertar isso, eventualmente. O que você está tentando realizar apontando para outro exemplo para um problema reconhecido?

Desculpe, eu só queria saber se alguém teria uma solução alternativa nesse meio tempo.
Você pode excluir minhas postagens, se necessário.

Eu crio app com Framework7 e React, porém o formato html do framework7 é fixo,

<div class="page"><div class="navbar"></div><div class="searchbar"></div><div class="page-content"></div><div class="toolbar"></div></div>

Não consigo envolver os elementos filho de primeiro nível (navbar, searchbar) em outro div que não tenha a classe 'page'

Eu tenho um componente que retorna uma lista de linhas

para ser usado em uma tabela e não posso envolvê-los em uma tag HTML adicional (não permitido pelo padrão HTML5 - eu sei sobre tbody, mas posso ter várias linhas retornadas por vários componentes filhos que podem precisar ser combinados em um único tbody ). A técnica mencionada por @Prinzhorn - envolvendo essas crianças em um comentário HTML - foi realmente implementada por alguém? Tentei implementar um componente que renderiza apenas o comentário HTML, mas não parece funcionar.

Para sua informação, a reescrita em que estamos trabalhando (#6170) já suporta fragmentos. Você pode acompanhar nosso progresso em #7925 e em http://isfiberreadyyet.com.

O simples fato de que o elemento <table> não pode renderizar certos elementos é motivo suficiente para adicionar esse recurso para ser compatível com as APIs da web.

EDIT: Ah, fragmentos! Ansioso para eles.

@trusktr

Conforme observado no primeiro post deste tópico:

Nota dos mantenedores:

Sabemos que isso é um problema e sabemos exatamente qual conjunto de problemas pode ser resolvido . Queremos isso também, mas é um problema difícil com nossa arquitetura atual. Comentários adicionais expressando desejo por esse recurso não são úteis.

😉

Caramba, eu tenho que parar de fazer isso.

Eu votei isso, mas queria compartilhar um caso de uso válido. Suponha que eu tenha 3 botões que eu quero um layout líquido CSS (esquerda, centro, direita). O botão esquerdo está sempre visível, mas os outros dois são condicionalmente visíveis. Em React (especificamente React Native), estou usando seu flexbox e renderizando como:

[ Left ] { renderCenterAndRightButtons(this.state.someFlag) }

Os botões central e direito não são posicionados corretamente porque estão agrupados em uma View singular.

Sim, eu poderia dividir os botões central e direito para seus próprios métodos, mas isso introduziria muito código redundante, pois eles compartilham muito comportamento.

[ Left ] { renderCenterButton(this.state.someFlag) } { renderRightButton(this.state.someFlag) }

Como dito acima, já estamos cientes dos casos de uso desse recurso e estamos trabalhando duro para apoiá-lo. (Já há meia dúzia de pessoas acima chamando a atenção para o problema do flexbox.) Como não parece que mais discussões serão produtivas, estou bloqueando isso. Atualizaremos quando tivermos notícias sobre a nova implementação.

Acho que podemos fechar isso.

O retorno de arrays de componentes é suportado desde o React 16 Beta 1, que você pode experimentar agora .

Ainda existem algumas limitações (o suporte SSR não está pronto), mas estamos rastreando-as em #8854 e corrigimos antes da versão 16 final.

Obrigado a todos pelo feedback!

OBRIGADO DANI

🍾🍾🍾

🦏

@gaearon Melhoria incrível! É suporte para nó de texto puro?

Sim, ele também suporta o retorno de strings.

Posso perguntar como isso deve funcionar?

Eu esperava que isso nos permitisse renderizar 2 elementos XJS adjacentes, mas se eu fizer return ["foo", "bar"] (algo mais útil;), recebo o esperado bundle.js:66656 Warning: Each child in an array or iterator should have a unique "key" prop.

Então, o recurso era uma maneira de não cercar uma lista real por um elemento estranho?

Então, o recurso era uma maneira de não cercar uma lista real por um elemento estranho?

Sim, uma maneira de fornecer vários elementos de uma renderização. (Para obter mais informações, consulte a postagem inicial do problema.)

Se todos os itens de seu array são conhecidos como strings, basta juntá-los e retornar uma única string. Caso contrário, você pode comer este aviso ou descobrir o que fazer para envolvê-los em elementos para que possam ser codificados para melhorar a reconciliação do DOM, assim como você faria se tivesse incluído um array de strings dentro de outro elemento em JSX.

@diligant retornando ['foo', 'bar'] não é relevante para este problema. Você nunca poderia e ainda não pode fazer return <div>{['foo','bar']}</div> Cada filho em uma matriz deve ter uma propriedade 'chave', seja em uma tag jsx interna como <div> ou seja retornada. O que você pode fazer agora é:

return [<div key='1'>foo</div>, <span key='2'>bar</span>];

Ele remove uma grande limitação em reagir.

Btw retornando ['foo', 'bar'] lhe dá um aviso e não um erro e para remover esse aviso você pode facilmente juntar essas strings ou se elas forem tags jsx e não strings você pode adicionar uma chave prop a elas. Portanto, não há limitação sobre o retorno de matrizes.

@JesperTreetop obrigado.
@sassanh , do seu exemplo, parece que eu fui muito "tímido" para usar as teclas "apenas" para evitar um <div> inútil (semanticamente) circundante. Você quer dizer que devo ir em frente e que não há nenhuma penalidade real de desempenho? Isso seria realmente uma melhoria REAL!

@diligant Duvido que introduza qualquer penalidade de desempenho, mas você não precisa de um div inútil ao redor, a menos que esteja em torno de strings e, se estiver em torno de strings, você pode evitar array e juntar strings. Se não for strings, você pode adicionar a chave ao componente. Por exemplo, se você tem dois componentes Foo e Bar você pode retornar [<Foo key='foo'/>, <Bar key='bar'/>] e não precisa cercar nem seus componentes (como sempre) nem seu array (graças a nesta nova versão, você precisava cercar o array antes de 16).

@sassanh legal então. Claro que meu caso de uso foi com vários componentes. Feliz!! ;)

Na verdade, parece-me estranho avisarmos sobre a falta de chaves quando todos os itens são strings. Eu não esperaria que fizéssemos isso. Esse comportamento existe em 15 quando as strings estão dentro de uma div? Caso contrário, devemos corrigi-lo em 16 para strings de nível superior (já que é impossível fornecer chaves a elas).

@gaearon desculpe, meu exemplo foi enganoso: eu quis dizer componentes.

Eu verifiquei e em -beta.5 , o React não emite um aviso ao renderizar um array com várias strings.

No entanto, estou intrigado com o requisito key ao renderizar um array apenas para evitar um componente ao redor. Eu entendo e não é nada, mas provavelmente levantará toneladas de perguntas no SO (não tenho nada melhor para oferecer…)

E por fim, obrigado novamente.

Este aviso é exatamente o mesmo que para qualquer outro uso de matrizes de componentes, porque se resume exatamente ao mesmo "trabalho extra" para o reconciliador e, portanto, à mesma otimização potencial definindo um key . Para mim, a surpresa seria se os arrays de componentes fossem tratados de maneira diferente dependendo disso, e imagino que isso também levaria a algumas perguntas do Stack Overflow. Sem mencionar que acho que envolveria alguma sobrecarga apenas para acompanhar isso em primeiro lugar.

.. e na maioria das vezes estarei retornando um array de uma função, eu quero o aviso key no lugar. Parece que as vezes que você não gostaria que o aviso fosse a minoria, e não há como o React saber se é apropriado não avisar.

O aviso é necessário porque a falta de chaves pode causar problemas de correção, não apenas problemas de desempenho. Isso é explicado em muitas outras questões perguntando por que as chaves são necessárias, então eu encorajo você a procurá-las e ler essas discussões. Concordo que os documentos poderiam ser mais claros sobre isso, e é algo que provavelmente veremos na próxima vez que fizermos um sprint de mudança de documentos.

Não há diferenças conceituais entre arrays retornados diretamente da renderização e arrays dentro de uma div. Portanto, não há razão para que haja um aviso importante em um caso, mas não no outro. Eles precisam funcionar da mesma maneira porque ambos são afetados pelos mesmos problemas quando as chaves estão faltando.

Dito isso, entendemos que é irritante especificar chaves para conteúdo estático . Assim como você não especifica chaves quando escreve uma estrutura JSX onde os filhos são conhecidos estaticamente (e, portanto, nunca reordenam), seria bom ter uma maneira de fazer isso com arrays.

No futuro, podemos resolver isso adicionando suporte explícito para fragmentos em JSX com sintaxe como:

return (
  <>
    <div>child 1</div>
    <div>child 2</div>
  </>
);

Ele poderia produzir uma matriz, mas atribuir índices numéricos implicitamente aos filhos porque, nesse caso, sabemos que eles nunca podem reordenar. Essa garantia dada pela expressão filho JSX é exatamente o que nos permite escapar sem especificar chaves no código JSX normal, onde um div pode ter vários filhos.

Mas ainda não temos essa sintaxe. Então, por enquanto, esta é uma limitação conhecida.

@JesperTreetop & @zwily , @gaearon explicou muito melhor do que eu ;)

Uma vez que você sabe, não é grande coisa, mas como todos nós queremos que o React prospere, eu estava apenas dizendo…

@gaearon Existe outro problema para a proposta de sintaxe <> que podemos assistir em vez de discutir mais sobre esse problema? Procurei por aí mas não encontrei nenhum.

@smrq +1 para questionar - eu sigo tudo sobre essas limitações estranhas (resultado rendrer() um para um, chaves e sintaxe ou fragmentos JSX), mas o único ticket que conheço é https://github.com/ facebook/jsx/issues/65

Eu também suponho que as fibras vão resolver o problema com as chaves - mas parece que é um sonho não realizado

As chaves não são um "problema". :) Eles são uma parte fundamental e necessária de qualquer framework de visualização que permite criar listas dinâmicas.

Consulte https://facebook.github.io/react/tutorial/tutorial.html#keys para obter uma explicação mais detalhada.

@spicyj , na verdade, é um «problema», pois causa gastos prolongados de energia dos desenvolvedores e há uma possibilidade fundamental de programar a estrutura de visualização sem tal requisito (por exemplo https://github.com/dfilatov/vidom)

existe a possibilidade fundamental de programar a estrutura de visualização sem tal requisito (por exemplo https://github.com/dfilatov/vidom)

vidom usa chaves em coleções. Pode tecnicamente funcionar sem ele, mas provavelmente seria muito mais lento. O React também pode tecnicamente funcionar sem chaves, mas seria bastante inesperado descobrir que metade de seus componentes precisa ser atualizado quando você remove um único item de uma lista. Com chaves, apenas um item é desmontado.

@goto-bus-stop vidom pode usar chaves, mas elas não são necessárias e sem elas apenas casos realmente grandes com muitas atualizações podem causar problemas reais de desempenho

então considero esta parte como possível opcional (como, por exemplo, shouldComponentUpdate ) que pode ser usada para ajustes de desempenho em casos individuais

Exemplo @veged de vidom lutando sem chaves .

Ele não tem ideia de que deve reordenar os elementos, então descarta as instâncias em cada renderização do componente raiz.

como alguém que está bastante familiarizado com o espaço virtual-dom [1]. eu posso dizer isso:

  1. as chaves são necessárias para atualizações previsíveis de dom e estado entre irmãos semelhantes.
  2. as chaves normalmente não são uma otimização e - na verdade - geralmente são o oposto.

[1] https://github.com/leeoniya/domvm

no entanto, o problema inequívoco aqui (como @gaearon descrito) é um uso totalmente estático de arrays e a diferença entre arrays estáticos e fragmentos JSX estáticos

@brigand O não tem dúvidas de que a reordenação de componentes de estado completo pode causar alguns problemas ;-) mas forçar todos os outros casos (na minha opinião mais deles) a lutar por isso ... parece controverso

As chaves são importantes para o React. Mesmo que eles não pareçam importar em alguns casos (por exemplo, porque os componentes abaixo são todos sem estado), não há nada que impeça você ou outra pessoa da equipe de adicionar um componente com estado (ou mesmo uma entrada DOM simples) em algum lugar alguns níveis abaixo em poucos meses. A essa altura, você pode esquecer que não tem chaves e, portanto, as reordenações podem fazer com que o estado (ou valor de entrada) seja associado a um item errado.

É por isso que recomendamos que você especifique chaves em todos os lugares para listas dinâmicas. Não fazer isso leva a erros muito difíceis de rastrear, e achamos que é melhor gastar dez segundos extras especificando a chave do que dez horas depurando por que o estado de um componente vários níveis abaixo fica confuso em algum caso de canto.

Concordo plenamente que é inconveniente especificar chaves quando a lista é conhecida por ser estática e nunca reordenada. Você está convidado a discutir isso no repositório JSX. Se você não encontrar um problema para isso, você pode criar um novo lá.

Como este tópico tem muitos assinantes e o recurso foi implementado, gostaria de bloqueá-lo para evitar enviar spam a muitas pessoas com notificações. Espero que os esclarecimentos acima respondam às suas preocupações, mas, caso contrário, você pode criar um novo problema para uma discussão mais aprofundada sobre um tópico específico.

@smrq criou um problema de proposta para a sintaxe <> no repositório jsx: https://github.com/facebook/jsx/issues/84.

Acabamos de liberar o suporte para uma nova exportação React.Fragment e a sintaxe <> associada:
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

Esta página foi útil?
0 / 5 - 0 avaliações