React: Ajouter une API de fragment pour permettre le retour de plusieurs composants à partir du rendu

Créé le 2 sept. 2014  ·  148Commentaires  ·  Source: facebook/react


Note des mainteneurs :

Nous savons qu'il s'agit d'un problème et nous savons exactement quel ensemble de problèmes peut être résolu. Nous voulons cela aussi mais c'est un _problème difficile_ avec notre architecture actuelle. Les commentaires supplémentaires exprimant le désir de cette fonctionnalité ne sont pas utiles. N'hésitez pas à vous abonner au problème (il y a un bouton dans la colonne de droite) mais ne commentez pas à moins que vous n'ajoutiez de la valeur à la discussion. "Moi aussi" et "+1" n'ont pas de valeur, pas plus que les cas d'utilisation déjà écrits dans les commentaires (par exemple, nous savons que vous ne pouvez pas mettre les éléments <tr> ou <dd> avec un <div> ).


Considérer ce qui suit:

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

});

Si vous supprimez le <div></div> dans le map , vous obtenez l'erreur suivante : _Les éléments XJS adjacents doivent être enveloppés dans une balise englobante_

ce n'est que lorsque je rajoute les divs environnants, et plutôt inutiles, qu'il compile sans problème. j'utilise la 0.11.1

Est-ce que cela est abordé? Il ajoute un supplément, et encore une fois - IMO - html inutile et inutile à la page, qui, tout en ne nuisant à rien - semble désordonné et non professionnel. Peut-être que je fais juste quelque chose de mal, s'il vous plaît éclairez-moi si je le fais.

Commentaire le plus utile

Je pense qu'on peut fermer ça.

Le retour de tableaux à partir de composants est pris en charge depuis React 16 Beta 1 que vous pouvez essayer maintenant .

Il y a encore quelques limitations (le support SSR n'est pas prêt) mais nous les suivons dans # 8854 et nous les corrigerons avant la version finale 16.

Merci à tous pour vos commentaires !

Tous les 148 commentaires

Parce que lorsque vous ne mettez pas l'emballage, cela désucre à ceci :

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

Ce qui n'a pas de sens syntaxique. La page du compilateur jsx peut vous aider si vous avez besoin d'un mappage visuel.

Cela étant dit, il est possible de le désucrer à [div, div] la place. C'est difficile, quelque peu controversé et ne sera pas mis en œuvre dans un avenir proche.

(Je ne pense pas que ce soit particulièrement controversé, mais cela ajoute de la complexité au code et n'a pas encore été fait.)

IIRC @syranide a quelques commentaires à ce sujet

@chenglou Hehe.

J'en ai eu une brève discussion avec chenglou il y a peu, je ne penche pas vraiment d'un côté ou de l'autre pour le moment. Je vois beaucoup de dangers cachés à permettre à un composant composite de renvoyer plusieurs composants et cela casse beaucoup d'hypothèses intuitives, mais je ne connais aucun cas d'utilisation (pour le moment) de bonne pratique qui en bénéficierait évidemment.

La simplicité de renvoyer au plus un composant signifie qu'il est très facile de raisonner sur ce que vous voyez, sinon, <table><TableHeader /></table> pourrait en fait rendre n'importe quel nombre de lignes, vous n'avez aucun moyen de savoir autre que d'inspecter TableHeader et quels que soient les composants composites qu'il renvoie.

J'ai l'impression que le fait de pouvoir renvoyer plusieurs composants ne fait que déplacer la responsabilité d'envelopper les composants si nécessaire du "composant composite" à tout ce qui "rend le composant composite". Le composant "qui rend le composant composite" a rarement ou devrait savoir s'il faut ou non envelopper des composants composites, alors que les enfants sont plus susceptibles de connaître leurs parents.

Mais peut-être s'agit-il simplement d'un cas de responsabilité du développeur. Il peut y avoir de bons cas d'utilisation pour les deux et nous devrions simplement regarder au-delà de l'inévitable mauvaise utilisation.

@sebmarkbage a probablement aussi quelques commentaires :)

Nous n'autoriserons probablement jamais cette syntaxe implicitement. Vous auriez besoin d'un emballage comme

<>
  <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>
]

Cependant, même cela ne fonctionne pas. Souvent, c'est probablement pour le mieux. Cela peut être déroutant pour la consommation de composants lorsqu'un enfant peut se développer en plusieurs éléments.

Mais, vraiment, la seule raison pour laquelle nous ne soutenons pas cela pour le moment est parce que c'est difficile à mettre en œuvre. Espérons que nous serons en mesure de le soutenir dans le futur, mais probablement pas à court terme. Pardon. :/

cela ne peut-il pas affecter des choses comme jquery ou d'autres bibliothèques qui ciblent des éléments spécifiques, donc si vous faites quelque chose comme $('#id-name').children() , ce qui suit :

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

les <div> et <div class="class-name"> seraient sélectionnés dans ce cas. (si j'ai bien compris)

Cela affecte également les sélecteurs CSS de la même manière que @AdamKyle a publié auparavant.

Des mises à jour sur ce problème ?

J'ai passé quelques minutes à comprendre pourquoi mon composant ne fonctionnait pas. J'ai l'impression qu'il devrait y avoir une notice quelque part, peut-être que je l'ai raté ? Peut-être qu'il est évidemment faux d'essayer :

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 Vous devriez avoir une erreur de compilation JSX avec l'erreur "Les éléments XJS adjacents doivent être enveloppés dans une balise englobante" ; n'avez-vous pas vu l'erreur ou n'était-elle pas claire dans son explication ?

Merci pour votre réponse @spicyj. Eh bien, je voulais dire un avis dans la documentation de React. Oui, la console a affiché une erreur, mais la nécessité d'envelopper n'avait pas de sens pour moi au début. C'est pourquoi j'ai cherché et je suis arrivé ici.

J'ai aussi eu cette douleur... particulièrement douloureuse pour mon créateur, d'ailleurs. Ce serait plutôt bien si un composant pouvait générer un nœud (donc une liste de nœuds ou un fragment) au lieu d'un élément.

Je dis juste .. Je ne préconise pas de renvoyer plusieurs enfants du composant _mais_ j'aimerais le faire dans les méthodes render* que j'extrait 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>
        }
      </>
    );
  }

Mais je devrais probablement me taire et utiliser key s.

@gaearon Vous pouvez déjà le faire, il vous suffit de renvoyer un tableau pour l'instant (ce qui est un peu lourd mais oui) ... buuuuut, vous pouvez contourner cela, j'ai piraté mon propre composant <Frag> qui est traduit en un tableau (surchargé React.render ) ... vous pouvez aussi faire return <NoopComp>...</NoopComp>.props.children je suppose, si vous voulez éviter les hacks.

EDIT: Mon mauvais, j'ai surchargé React.createElement pas React.render .

Le problème avec les tableaux est qu'ils font trébucher notre concepteur. Besoin de virgules, de clés explicites.

@gaearon Ouais, vous pouvez éviter les virgules en utilisant l'une de mes deux solutions de contournement pour l'instant (si vous trouvez l'une ou l'autre acceptable) ... mais que voulez-vous dire avec des clés explicites?

Si j'utilise la syntaxe de tableau, je dois spécifier key sur chaque élément. Non pas que ce soit difficile à faire, mais c'est gênant parce que je sais qu'ils ne changent jamais.

@gaearon Ah oui, je choisis d'ignorer mentalement cet avertissement pour l'instant :), si vous voulez vraiment l'éviter, vous pouvez faire <MyComp children={this.renderWhatever()} /> pour l'éviter ( EDIT: bien que vous ne puissiez évidemment pas l'utiliser si vous avez des enfants adjacents, vous pouvez utiliser une aide à l'aplatissement... mais oui).

Un autre cas que j'ai rencontré avec un kit d'interface utilisateur. Lorsque vous placez des enfants dans un conteneur déroulant fixe comme ceci :

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

Les enfants doivent maintenant être passés sous forme de tableau, mais si une classe composite est passée, elle doit également implémenter ces styles pour éviter de la casser. Ajoute juste une couche de complexité. Mais je peux tout à fait comprendre à quel point il doit être complexe de faire le changement. Je lance juste mon chapeau pour le soutien.

J'ai un autre cas d'utilisation que j'ai décrit en détail ici #3415. Mais je peux contourner le problème pour l'instant et comprendre qu'il est difficile à mettre en œuvre et rare.

Je ne sais rien (encore) sur les internes de réaction, mais je vais juste donner une idée de la façon dont vous pourriez marquer de tels éléments/fragments parents virtuels dans le DOM : commentaires. Par exemple

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

rendrait à

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

Cela signifie que c et d seront traités comme des enfants de qch. (y compris le reactid imbriqué pour ne pas entrer en conflit avec b et e). J'ai vu d'autres frameworks "abuser" des commentaires pour ce genre de travaux sémantiques.

@Prinzhorn Je pense que vous confondez peut-être la sortie DOM avec JSX de React.

Au cas où cela serait utile : la suggestion de @Prinzhorn d'utiliser des commentaires HTML pour mapper des "composants de fragment" au DOM est la même approche que celle utilisée par Knockout. Knockout appelle ces "éléments virtuels".

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

(à partir de docs knock -out)

En outre, un autre cas d'utilisation pour cela - les éléments d'emballage supplémentaires peuvent être problématiques lors de l'utilisation de flexbox.

@aldendaniels est absolument d'accord, j'ai déjà rencontré des problèmes de flexbox.

J'ai rencontré cela avec flexbox avec des listes déroulantes dépendantes. Où A rendrait et gérerait la première liste déroulante, suivie de B ou C ou D selon la valeur de la liste déroulante de A, qui rendraient chacune le nombre approprié de listes déroulantes, par exemple

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

A, B, C et D sont sans état, je les ai donc changés de class B {render(){ this.props }} à function B(props){ return [...]; } .

Dans ce cas, peu importe au parent que plusieurs enfants soient rendus, c'est juste pour gérer mon CSS. Il y a des choses que je ferais différemment si elles n'avaient pas besoin d'être réutilisées (un autre composant a besoin de B, C et D).


Au lieu de cela, peut-être un moyen de le démêler du parent ? Je n'ai pas d'idées précises sur ce à quoi cela ressemblerait.

J'ai rencontré un scénario aujourd'hui qui, à mon avis, est un bon cas d'utilisation pour cette fonctionnalité : un composant qui rend plusieurs éléments <script> , où il pourrait être rendu dans l'élément <head> de la page. Tout élément d'emballage serait mauvais.

Mon scénario veut avoir un composant qui est responsable du rendu de la balise <script> à la fois pour le code d'exécution nécessaire sur la page ainsi qu'une autre balise <script> qui porte les chaînes localisées à utiliser par le code d'exécution. Par exemple:

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

Dans ce cas, j'aimerais que le code soit créé comme suit :

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

J'ai également rencontré des problèmes de flexbox. Rien qui ne puisse être résolu en CSS, mais l'une des "beautés" de flexbox est que vous avez besoin de moins d'éléments "wrapper" partout pour que votre mise en page fonctionne, mais vous vous retrouverez toujours avec des éléments wrapper partout lorsque vous utilisez React depuis vous enveloppez toujours tout ce que vous retournez en div/div ou similaire, à moins qu'il soit logique d'avoir un conteneur.

Pour tous les cas d'utilisation présentés ici, je suis à peu près sûr que vous pourriez remplacer <BunchOfComponents /> par {getBunchOfComponents()} et la sortie visuelle serait la même, sans introduire les problèmes pratiques et techniques liés aux composants avec fragments comme racine.

@syranide mais à chaque fois qu'un des composants change tous ses frères et soeurs doivent être recalculés...

De plus, si vous utilisez un coffeescript simple, il est facile de renvoyer un tableau, veuillez donc découpler la fonctionnalité de la représentation jsx.
IOW s'il est facile de gérer un tableau d'éléments renvoyé, n'attendez pas que jsx rattrape son retard.

@syranide mais à chaque fois qu'un des composants change tous ses frères et soeurs doivent être recalculés...

@wmertens Oui, mais plusieurs fois, vous auriez cela de toute façon parce que le parent aurait besoin de restituer pour d'autres raisons, ou simplement parce que vous recevez les données via des accessoires de toute façon. Mais oui, c'est la différence, mais cela ne signifie pas que cette approche est la bonne, c'est une optimisation et il existe de nombreuses façons de les accomplir.

De plus, si vous utilisez un coffeescript simple, il est facile de renvoyer un tableau, veuillez donc découpler la fonctionnalité de la représentation jsx.

Ce n'est pas pertinent et ce n'est pas un problème avec JSX. Un gros problème est que vous perdez l'hypothèse technique, pratique et intuitive d' un composant = un élément/nœud . Je ne peux pas parler pour les développeurs mais je n'abandonnerais pas cela volontairement, c'est une hypothèse très utile à avoir. Je suis sûr qu'il existe des optimisations tout aussi bonnes ou meilleures qui pourraient être conçues si l'optimisation est la seule raison pour laquelle les gens veulent cela.

@syranide le plus gros problème c'est qu'on ne peut pas toujours utiliser un emballage
élément en html, comme dans les tableaux, listes, flexbox, head...
cela conduit à un code laid.

Je serais parfaitement heureux avec un élément wrapper virtuel qui ne rend que
commentaires, comme suggéré précédemment.

Le ven. 29 mai 2015, 15 h 56 Andreas Svensson [email protected]
a écrit:

@syranide https://github.com/syranide mais à chaque fois l'un des
les composants changent tous ses frères et sœurs doivent être recalculés...

@wmertens https://github.com/wmertens Oui, mais souvent vous le feriez
avoir cela de toute façon parce que le parent aurait besoin de restituer pour d'autres
raisons, ou simplement parce que vous recevez les données via des accessoires de toute façon. Mais
oui, c'est la différence, mais cela ne signifie pas que cette approche est la bonne,
c'est une optimisation et il y a plusieurs façons de les accomplir.

De plus, si vous utilisez un coffeescript simple, il est facile de renvoyer un tableau, alors s'il vous plaît
découpler la fonctionnalité de la représentation jsx.

Ce n'est pas pertinent et ce n'est pas un problème avec JSX. Un gros problème est que
vous perdez l'hypothèse technique, pratique et intuitive de _one
composant = un élément/nœud_. Je ne peux pas parler pour les développeurs mais je ne le ferais pas
abandonner cela volontairement, c'est une hypothèse très utile à avoir. Je suis sûr
il y a des optimisations tout aussi bonnes ou meilleures qui pourraient être conçues si
l'optimisation est la seule raison pour laquelle les gens veulent cela.


Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/facebook/react/issues/2127#issuecomment -106810565.

fwiw, il est relativement facile de pirater un composant "fragment" dans React qui est traité comme un tableau de ses enfants par React. Il générera automatiquement des clés, mais comme cela se produit après la validation initiale des composants, il ne générera pas l'erreur habituelle "aucune clé fournie".

Cela dit, ce hack ne résout que ce dont @gaearon parlait ci-dessus - ne pas avoir à gérer la syntaxe de tableau laide/la définition de clés arbitraires dans votre JSX - et non le problème de renvoyer plusieurs nœuds à la racine de la méthode de rendu d'un composant.

J'ai un problème avec l'idée qu'un composant doit renvoyer un "élément/nœud". Pour moi, cela semble parfaitement raisonnable pour une structure JSX de :

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

pour finir comme le DOM :

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

Je ne pense pas qu'il s'agisse d'une violation du principe de la moindre surprise, car les composants font déjà toutes sortes de "choses surprenantes" avec le DOM en utilisant des crochets de cycle de vie (il suffit de regarder le modèle de trou de ver commun). Il est généralement admis qu'un composant n'entraînera pas nécessairement la création d'un seul élément, et c'est bien, car certains compromis doivent être faits pour fonctionner avec le DOM.

Il ne s'agit pas non plus d'"optimisations", ou même de ne pas aimer la syntaxe des tableaux. Comme de nombreux utilisateurs l'ont mentionné, les éléments _wrapper cassent sérieusement le style et la mise en page_. Les tableaux sont les plus évidents, mais Flexbox est également un problème majeur. J'ai déjà du CSS qui réapplique simplement les règles flexibles aux éléments wrapper qui n'existent qu'à cause de React, et c'est assez moche.

Pour tous les cas d'utilisation présentés ici, je suis presque sûr que vous pourriez remplacer avec {getBunchOfComponents()} et la sortie visuelle serait la même, sans introduire les problèmes pratiques et techniques liés au fait d'avoir des composants avec des fragments en tant que racine.

Cela oblige les développeurs à faire des compromis sur la création de composants isolés et réutilisables - le ciel les aide s'ils décident de réutiliser leur groupe de composants ailleurs - en raison d'un problème d'implémentation sous-jacent dans React. Je ne pense pas que cela doive être accepté.

@thomasboyt

EDIT : Mon erreur, j'ai confondu certains de vos arguments avec la table de discussion ci-dessus, je suis largement d'accord avec ce que vous dites, je pense. Mais il y a toujours des problèmes avec les composants étant opaques, donc ce qui est conçu comme un wrapper transparent utile devient opaque pour le parent. Imaginez <Wrapper1><Wrapper2>...</Wrapper2></Wrapper1> , Wrapper1 ne puisse pas voir les enfants de Wrapper2 . Alors peut-être wrapMyElements(...) est simplement une meilleure solution globale (y compris toute autre fonctionnalité de support nécessaire).

J'ai un problème avec l'idée qu'un composant doit renvoyer un "élément/nœud". Pour moi, cela semble parfaitement raisonnable pour une structure JSX de :

Les composants sont plus que de simples emballages stupides, ils ont un but. À mon humble avis, il semble que le retour de plusieurs éléments bloque certaines attentes très utiles. Par exemple, React.render obtiendra un compagnon dans le futur qui rendra un élément et retournera les nœuds, cela doit maintenant produire un tableau de nœuds à la place.

Mais je pense qu'un problème très important est celui de la lisibilité qui, à mon humble avis, est le plus gros argument de vente de React, tout est explicite.

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

En regardant cela, cela n'a aucun sens, d'où vient la 3ème cellule ? Peut-être que c'est en fait faux et que ça rend 2 ou 4 cellules, qui sait, peut-être que c'est en fait dynamique et dépend d'un accessoire ou d'un état externe? Il existe de nombreuses variantes de ce problème qui ne font que s'aggraver lorsque vous considérez d'autres interfaces non-HTMLDOM qui peuvent avoir des attentes explicites. Une autre chose à considérer est que les éléments sont opaques, donc si vous remplacez <tr /> par <MyMagicalTr /> alors il n'est pas capable d'interagir avec les cellules individuelles ou même de déduire combien il y en a, donc même si <MyMagicalTr /> ne peut accepter que <MyMagicalTd /> , il n'y a aucune garantie qu'il puisse réellement interagir avec eux.

Cela oblige les développeurs à faire des compromis sur la création de composants isolés et réutilisables - le ciel les aide s'ils décident de réutiliser leur groupe de composants ailleurs - en raison d'un problème d'implémentation sous-jacent dans React. Je ne pense pas que cela doive être accepté.

"Cela oblige les développeurs à faire des compromis pour rendre isolé...", mais c'est exactement le problème si vous me demandez, si un composant peut renvoyer plusieurs éléments, il n'est plus isolé, il est remplacé, le composant fuit dans le parent.

Planter des clous. Le fait qu'il s'agisse d'une question de mise en œuvre sous-jacente est une question distincte de celle de savoir si cela devrait être fait ou non. Ce n'est pas ma décision, mais je ne vois pas comment un cas d'utilisation rare est un argument convaincant sans tenir compte des compromis qui l'accompagnent ou des autres solutions alternatives qui existent.

À mon humble avis, je ne vois pas le problème avec {getBunchOfComponents()} , c'est explicite, cela nous permet de garder nos attentes utiles. Si les performances sont un problème, alors React.createSmartFragment() (ou w/e) à la rescousse, un type de type tableau/objet transparent mais qui peut se mettre à jour indépendamment de son parent.

Encore une fois, les développeurs de React sont l'autorité (pas moi), mais je ne vois pas d'argument convaincant ici compte tenu des divers effets secondaires. Je ne suis même pas sûr d'être d'accord avec le fait que la solution présentée soit un bon modèle, même si elle était prise en charge.

EDIT: Pour clarifier, peut-être que les composants pourront renvoyer plusieurs éléments à l'avenir car il existe d'autres cas d'utilisation manifestement bénéfiques, en particulier dans le contexte du passage par les enfants (comme celui que vous montrez @thomasboyt), la lisibilité est maintenue.

Je pense que j'aurai besoin d'un peu plus de café avant de pouvoir répondre au côté philosophique de cette conversation (merci pour les très bons points, @syranide), mais côté implémentation, j'ai commencé à fouiller cette nuit dernière pour voir comment viable un changement de cette portée est, conduisant à ce pic : https://github.com/facebook/react/compare/master...thomasboyt :fragment

Et a lancé une petite démo ici : http://www.thomasboyt.com/react-fragment-demo/

Quelques observations du côté de la mise en œuvre des choses :

  • Sans surprise, il est très difficile de moderniser un système qui s'attend à ce que "1 composant = 1 nœud" supporte plus de nœuds ;)
  • J'ai d'abord pensé à essayer de suivre les fragments du côté des opérations DOM, afin que les instructions de mutation générées par ReactMultiChild puissent rester les mêmes et traiter les fragments comme n'importe quel autre nœud. Cependant, je ne pouvais pas penser à un bon moyen d'ajouter un état sur le nombre de nœuds/quels nœuds sont des fragments dans le suivi de l'état DOM. Quelque chose comme le commentaire clôturant @Prinzhorn noté pourrait fonctionner, mais je me méfie de tout ce qui nécessiterait une recherche DOM, compte tenu du coût relatif.
  • Avec cette idée rejetée, j'ai ajouté un champ _nodeCount à tous les enfants d'un composant ReactMultiChild , afin qu'il puisse suivre le nombre de nœuds racine qu'un fragment contient réellement.

Le problème est que, bien que cela soit assez facile à faire sur un rendu initial en comptant simplement les enfants d'un fragment, la mise à jour du nombre de nœuds du fragment sur les mutations ultérieures semble plus délicate. Ce n'est toujours pas fait sur ma branche (voir https://github.com/thomasboyt/react/issues/2).

  • De nombreuses opérations DOM reposent sur l'accès au nœud parent d'un élément, recherché par l'ID de nœud interne, pour ajouter/déplacer/supprimer des éléments (voir https://github.com/thomasboyt/react/issues/3). Étant donné que le cycle updateComponent ReactMultiChild est responsable de la transmission de cet ID, il pourrait être modifié pour effectuer une recherche du parent le plus proche qui a un nœud DOM, mais cela semble coûteux. Alternativement, il peut être possible d'avoir un registre interne de clés de fragment pour leurs clés de "nœud réel".

Je ne suis toujours pas convaincu qu'exiger des fragments qu'ils maintiennent un nombre de leurs nœuds racine est la meilleure façon de le faire (bien que cela m'ait au moins conduit à cette démo), et tout cela a été piraté assez rapidement et assez tard dans la nuit , donc si quelqu'un d'autre a une suggestion d'implémentation, n'hésitez pas à intervenir :>

@thomasboyt IIRC le principal obstacle à l'implémentation vient de React référençant les nœuds enfants par mountIndex , cela ne fonctionne pas lorsqu'un "nœud" peut soudainement devenir n'importe quel nombre de nœuds et cela peut se produire sans appeler le parent et cela peut aussi arriver plusieurs composants en profondeur (emballage). Si je ne me trompe pas, il est assez trivial que React prenne en charge plusieurs éléments racine tant que le nombre ne change jamais.

Donc, je ne pense pas qu'il serait particulièrement difficile de le faire fonctionner dans React, mais une solution vraiment appropriée est plus problématique et devrait probablement impliquer de supprimer mountIndex .

@syranide Droit ; la solution sur laquelle je travaille introduit en fait un nouveau nodeIndex qui est censé être le "décalage réel" d'un nœud (ce qui me rappelle que je dois revenir en arrière et supprimer mountIndex , puisque Je pense qu'il est maintenant inutilisé dans ma branche).

Mais, comme vous le notez, cela pose problème si le nombre d'éléments racine change, car le nodeIndex d'un composant doit être mis à jour chaque fois que le nombre de nœuds d'un composant frère précédent change. Encore faut-il trouver une solution à cela.

J'ai également rencontré des problèmes de flexbox. @syranide pourriez-vous s'il vous plaît élaborer un peu plus sur votre solution proposée "getBunchOfComponents" ? Étant nouveau sur React, il est difficile de bien comprendre où définir cette fonction / comment l'appliquer.

@landabaso

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

Hé,

Je n'ai pas lu tout le fil, mais voici un cas d'utilisation d'optimisation du rendu qui peut nécessiter cette fonctionnalité :

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

Si cette fonctionnalité est publiée, ReactCSSTransitionGroup n'aura plus besoin d'un nœud wrapper, n'est-ce pas ?

@slorber Oui, c'est probablement vrai.

Courez dans le besoin de cette fonctionnalité tous les jours.

Si vous avez de nombreux petits composants (c'est-à-dire une conception très modulaire), vous finissez par devoir envelopper toutes sortes de choses dans des divs qui ne devraient pas l'être. Je confonds peut-être, mais je pense que cela se rapporte à cette question.

Pour <div> , vous pouvez les envelopper dans un <div> , mais pour les éléments de lignes de tableau <tr> , ce n'est pas si facile. Vous pouvez envelopper <tr> dans <tbody> , mais il n'est peut-être pas souhaitable d'avoir plusieurs couches de <tbody> enveloppant plusieurs couches de <tr> .

Le scénario que j'ai appelé essayait d'avoir un composant qui fournit des éléments <link> et <script> sans avoir à devenir entièrement le moteur de rendu <head> .

J'ai ajouté une note en haut de ce problème. Merci de le lire avant de commenter. https://github.com/facebook/react/issues/2127#issue -41668009

Bump... J'en ai un gros besoin côté serveur. Il est très compliqué de restituer des pages Web complètes (hors doctype) sans pouvoir restituer des fragments, à cause de la section <head> . Je travaille actuellement autour de cela via des mixins et un peu de logique dans le rendu final, mais ce serait beaucoup plus simple s'il y avait un support pour le rendu de plusieurs composants.

@impinball , vous pouvez essayer d'écrire quelque chose de similaire à react-document-title basé sur react-side-effect pour résoudre ces problèmes. J'ai pu faire la même chose pour les balises méta, les en-têtes, le titre et parfois les redirections

Je rencontre également ce problème, y a-t-il des solutions de contournement pour le moment? Je n'ai pas pu faire fonctionner {getBunchOfComponents()} comme suggéré.

Aucune autre que celles déjà citées.

@jonchay Vous pouvez créer un composant qui ne rend que ses enfants.

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

puis pour l'utiliser :

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

@whatknight Cela ne fonctionnera pas sauf dans les cas où return renderABunchOfComponents(); fonctionne déjà.

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

Il devrait au moins y avoir un moyen de renvoyer plusieurs fragments lors de l'interpolation et de "l'assemblage". L'exemple ci-dessus se plaint du fait que img et h1 sont adiacents, mais ils finiront de toute façon par se trouver dans le wrapper principal. C'est un élément d'emballage dont j'aimerais pouvoir me débarrasser.

@kilianc dans ce cas, vous pouvez simplement écrire

      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>,
      ]

la façon dont vous l'utilisez, cela ne fera aucune différence si ce problème est résolu.

J'ai besoin de cette fonctionnalité pour des raisons déjà indiquées, j'ai donc essayé d'implémenter un conteneur <frag></frag> sur https://github.com/mwiencek/react/tree/frag-component

La mise en œuvre n'est pas vraiment jolie, mais si cela fonctionne pour les gens, je peux soumettre un PR et laisser les développeurs de React le déchirer.

@mwiencek On dirait que votre implémentation ne fonctionne pas si le nombre d'enfants dans un fragment change dans une mise à jour (_nestedChildCount est défini uniquement dans mountComponent) ? Il y a une petite astuce pour que tout fonctionne bien. Tu as pourtant l'air d'avoir pris un bon départ. En fait, j'y ai repensé récemment et j'ai peut-être trouvé un moyen robuste d'y parvenir. Je ferai un retour si je trouve le succès.

@spicyj yup, tu as raison, je vais devoir me pencher là-dessus...

Super content que nous puissions voir une mise en œuvre appropriée bientôt, cependant. :) N'hésitez pas à copier les tests de cette branche s'ils sont d'une quelconque utilité.

@spicyj La voie à suivre n'est-elle pas d'utiliser createFragment et de transformer JSX en cela? Ou voulons-nous vraiment que les fragments soient des éléments ?

Pour construire et développer le dernier commentaire de @syranide , il semble qu'il n'y ait pas besoin d'une "API de fragment" supplémentaire si le rendu autorise les tableaux comme valeur de retour. JSX pourrait transformer plusieurs éléments racine en un tableau, ce qui fonctionnerait également pour les valeurs de retour de toute autre fonction. Ainsi, au lieu d'introduire une surface d'API supplémentaire, qui nécessite de la documentation et de l'apprentissage, l'une des limitations de React pourrait simplement être supprimée.

Cela affecterait au moins babel-plugin-transform-react-jsx (implémentation) et aussi babel-plugin-syntax-jsx (suppression de l'erreur d'analyse pour les éléments racine adjacents). Bien que changer le premier semble être assez sûr, je ne connais pas la portée / l'utilisation du second et l'impact que le changement proposé aurait sur d'autres projets.

Cela ne couvre toujours pas le cas d'utilisation d'un conditionnel avec plusieurs éléments. Je ne considère pas "Utiliser un tableau et ajouter manuellement un key={...} arbitraire à chaque élément" comme une solution à long terme.

d'accord avec @dantman

ouais, bon point. La génération automatique de clé doit être intégrée via la transformation. L'utilisation de l'index du tableau comme clé devrait suffire, car les éléments ne changent pas.

En ce qui concerne les conditions, cela pourrait également être intégré à la transformation ou vous pouvez également utiliser JSX-Control-Statements . Implémenté là de cette manière, d'où l'idée.

Pour gérer correctement les mises à jour, j'ai pensé que la solution à laquelle @spicyj pensait pour # 5753 pourrait également fonctionner pour les fragments (envelopper le contenu dans quelque chose comme <!-- react-frag: 1 --><!-- /react-frag: 1 --> ). Oui, les commentaires sont un peu laids, mais c'est beaucoup plus fiable que ce que j'essayais de faire avec _nestedChildCount . Cette approche est maintenant utilisée sur https://github.com/mwiencek/react/tree/frag-component

Je n'ai pas vu cela mentionné dans le fil jusqu'à présent, mais je pense que résoudre ce problème améliore également la composabilité. Par exemple, imaginez que vous ayez une grille dans laquelle vous souhaitez que les cellules s'estompent dans un certain ordre. Idéalement, il y aurait deux composants en jeu ici : un pour gérer la mise en page et un autre pour gérer l'animation. Vous auriez une API comme celle-ci :

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

Cela vous permettrait de passer à une mise en page différente ou à une animation différente, sans que l'un ait à connaître les détails de mise en œuvre de l'autre. GridLayout s'attendrait à recevoir une liste d'enfants. FadeAnimator intercepterait cette liste, injecterait les styles et/ou les écouteurs d'événements appropriés, et renverrait la nouvelle liste pour GridLayout à consommer. Il n'y a aucune raison pour que FadeAnimator se préoccupe de la mise en page d'une grille, sauf que les éléments React ne peuvent pas renvoyer les Arrays à partir du rendu. De plus, il n'y a pas de moyen simple de remplacer la grille par, disons, une disposition en maçonnerie, car FadeAnimator doit servir de conteneur pour ses enfants.

Avec les limitations actuelles, je suppose que vous pourriez faire quelque chose comme ceci :

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

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

mais cela rend le code moins clair, plus complexe et plus difficile à composer.

Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!
Ajoutez une API de fragment pour permettre le retour de plusieurs composants à partir du rendu!

La suggestion de @texttechne est meilleure. Au lieu d'introduire une API supplémentaire, react devrait gérer plusieurs éléments racine dans le rendu.

La gestion de plusieurs éléments racine dans le rendu serait difficile, je pense.
Cela signifie que là : https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L1089

Vous auriez un tableau d'éléments plutôt qu'un élément.
Pour cette raison, pour autant que je sache, vous devez maintenant instancier plusieurs éléments React : https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js# L471

Ensuite, montez plusieurs éléments React instanciés : https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L471

Et concilier tout le balisage produit dans le bon ordre.

Je pense que cela comporte des inconvénients qui pourraient ne pas vouloir contourner le processus de réconciliation.
Voulons-nous avoir des fragments comme éléments ou des fragments comme syntaxe de sucre autour des transformations ?

Je pense que ce fragment en tant qu'élément est correct, nous aurions juste besoin de créer un nouveau type de nœud interne similaire aux nœuds de texte ou aux nœuds vides, n'est-ce pas ? Bien que je ne sache pas comment nous les gérerions.

Par exemple, comment gérez-vous lorsque l'une des racines est démontée ? Comment gérez-vous correctement la mise à jour ?
Ou, comment gérez-vous plusieurs racines à l'intérieur des DevTools ? (réponse évidente : corrigez les DevTools...)

Je pense qu'un fragment est un composant composite. Où est la différence exactement ?
Si nous finissons par dupliquer le code afin d'implémenter des fragments, nous ferions mieux d'implémenter la syntaxe du sucre afin de garder les composants internes de React "vierges" ?

Je me demande, j'ai joué avec les composants internes de React autour de la question Subtree (renderSubtreeIntoContainer) et j'ai l'impression que c'est quelque peu lié. Lorsque vous souhaitez rendre dans un nouveau sous-arbre, vous devrez en fait rendre une nouvelle racine. Donc, si nous prenons en charge plusieurs racines au niveau de l'arborescence, rendons-nous de nouvelles racines à chaque fois :

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

entraînerait deux appels "rendre dans une nouvelle racine".

Plutôt qu'un seul appel si nous utilisions un wrapper, n'est-ce pas ? Qu'en est-il des performances ? Simplicité? Pour être honnête, mon sentiment est le suivant : nous ne devrions pas toucher aux composants internes de React pour gérer cette situation. Pouvons-nous plutôt réaliser cet exploit avec JSX ? Pouvons-nous améliorer la syntaxe JSX ?

(_Avis de non-responsabilité_ : je ne suis pas totalement habitué aux composants internes de React, et il se peut qu'il y ait certaines parties que je ne comprends pas entièrement ou que je n'ai pas comprises. Toutes mes excuses pour le malentendu.)

Edit : corriger/clarifier les choses. De plus, GitHub stylise mystérieusement les e-mails de manière étrange, j'ai donc dû reformater le bloc de code... :-(

Salut, contributeur/committeur principal de Mithril ici.

TL; DR : Les fragments sont extrêmement difficiles, même lorsque l'API et les éléments internes sont
Facile.

Au fait, je sais par expérience que c'est _très_ difficile à mettre en œuvre. Ce
a également été demandé à plusieurs reprises pour Mithril, mais a refusé en raison de
la pure difficulté. Toutes les tentatives de mise en œuvre ont échoué avec au moins
au moins un tiers de la suite de tests échoue.

Je travaille toujours sur les détails d'une bibliothèque vdom que je prévois d'écrire,
et il traitera tout comme un fragment, mais c'est quelque chose que vous avez
littéralement (ré)écrire la partie de rendu à partir de zéro pour. Comme Réagir,
il sera découplé du DOM, mais l'API sera sensiblement différente
conceptuellement pour le rendu.

Voici le hic avec les fragments : il faut les gérer complètement
en interne ou vous ne les différenciez pas correctement. Même
document.createContextualFragment est inutile. Juste à titre d'exemple, prenons
transformer deux arbres, rendu omis :

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

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

La transformation correcte pour cela devrait être de remplacer les éléments B et l'élément C , en laissant le reste intact. Comprendre cela n'est pas trivial, et vous devez essentiellement itérer les enfants du fragment tout en ignorant le fait qu'ils sont dans un fragment.

Mais quand le fragment est parti, vous devez gérer la sémantique du crochet, comme
shouldComponentUpdate (je ne me souviens pas du nom de React pour ce crochet). Alors
vous devez toujours suivre les fragments indépendamment. Vous diff leur
contenu comme s'il faisait partie de son fragment parent, mais vous avez toujours
pour garder une trace de la position de ce fragment pour le bien du composant.

Autrement dit, les composants ne sont plus intrinsèquement liés à leur
nœud DOM. Au lieu de cela, ils sont liés au fragment correspondant. React, comme la plupart des autres bibliothèques et frameworks vdom, couple intrinsèquement le composant à sa représentation arborescente, même avec les types attendus. C'est le moyen le plus simple d'implémenter un algorithme diff qui
gère les composants. Lorsqu'ils sont découplés, il faut les séparer
comptabilité pour les deux. Vous n'initialisez pas les composants lorsque vous initialisez
le nœud. Il s'agit désormais de deux processus complètement distincts. C'est difficile à faire
au départ, et encore plus difficile d'ajouter un support pour la suite.

Merci à tous pour les mots. Nous savons que c'est difficile et l'avons toujours sur notre liste de choses à faire. (Demander avec enthousiasme ne le fera pas arriver plus tôt @janryWang.)

@isiahmeadows FYI, la branche de réécriture de Mithril prend en charge les fragments.

@spicyj Vous êtes invités à jeter un œil à l'implémentation [1] [2] et aux tests [1] [2] si vous ne le suivez pas déjà. L'ensemble du moteur de diff n'est qu'à environ 400 LOC, donc il devrait être facile à suivre

@isiahmeadows Je pense que GitHub a mangé une partie de votre commentaire. Le bloc de code est cassé et je ne vois pas passer la première instance de <D /> .

Ça a l'air OK dans les e-mails. Peut-être avez-vous trouvé un bogue dans GitHub ?

Malheureusement, la gestion du démarquage de GitHub se comporte différemment lorsqu'un commentaire provient d'un e-mail. J'ai modifié le commentaire pour supprimer une ligne vide et maintenant il apparaît.

J'ai transféré l'e-mail d'origine à support@github. Espérons qu'ils puissent réparer l'analyseur. 😃

@lhorie Vous utilisez la syntaxe de tableau pour le fragment ?
avez-vous polyfill pour DocumentFragment ?

Et utiliser un "pseudo" élément wrapper, comme un commentaire HTML n'est pas une option ? Je pensais que c'était ainsi que les nœuds de texte étaient "résolus" ...

Merci d'avoir corrigé ce commentaire @spicyj

Pour répondre aux préoccupations soulevées par @isiahmeadows dans son exemple : le nouveau moteur Mithril ne suit _pas_ la sémantique suggérée par @isiahmeadows pour plusieurs raisons :

  • la mise en œuvre de ces sémantiques rendrait les différences _significativement_ plus complexes
  • cela rendrait difficile de raisonner sur les clés et les bogues liés aux clés, car il devient possible que les espaces clés saignent des composants et même dans les composants frères et sous-enfants.
  • cela rendrait les cycles de vie des fragments non intuitifs (par exemple, dans cet exemple, B.bar est supprimé, mais le fragment l'est aussi, et un nouveau fragment est créé pour envelopper C). Cela viole le principe général selon lequel les cycles de vie "cascade", ce qui signifie que vous ne pouvez plus être sûr qu'un nœud enfant est supprimé si un nœud parent donné est supprimé. Comme le point précédent, cela a le potentiel de provoquer des fuites sur les capacités d'encapsulation d'un composant.
  • si, hypothétiquement, on rencontre des problèmes de diff liés à un moteur de diff qui n'adhère pas à cette sémantique, la solution de l'espace d'application est aussi triviale que d'envelopper un fragment autour du nœud incriminé.

Je serais intéressé si l'équipe principale pouvait développer la note en haut : _pourquoi_ c'est difficile avec l'architecture actuelle. Étant donné que React et le moteur de rendu de Mithril tentent fondamentalement de résoudre les mêmes problèmes, et que Mithril prend désormais en charge les fragments à un degré que je pense faisable et utile, peut-être que l'implémenter dans React pourrait être plus faisable si différents aspects de la sémantique sont évalués séparément (et potentiellement rejetés) comme cela a été fait avec Mithril.

Notez que j'ai corrigé mon commentaire. J'ai fait quelques erreurs et GitHub ne stylise pas bien les réponses aux e-mails... :frowning:

@Primajin Je me suis aussi posé la question, mais je soupçonne qu'ils seraient passés comme un seul élément. Il est important de rendre les fragments composables (voir mon exemple ci-dessus ). Cependant, il y a probablement des moments où vous voudriez aussi les traiter comme une seule unité.

Peut-être que React.Children.map devrait étendre les fragments. Si vous souhaitez itérer sur chaque enfant (y compris les enfants des fragments enfants), utilisez Children.map . Si vous souhaitez traiter les fragments comme une boîte opaque, travaillez directement avec props.children comme un {tableau, élément}.

@lhorie Je n'ai pas été aussi impliqué dans la réécriture, donc je ne connais pas aussi bien ses subtilités. J'ai été occupé par le fait que j'ai une finale cette semaine et trois la semaine prochaine, en plus je travaille avec quelqu'un pour mettre en place un stage dont mon collège a besoin. Je me suis également concentré sur la finition de Techtonic, ce que j'ai _presque_ fait faire en CLI (un test est cassé alors qu'il ne devrait pas l'être).

@isiahmeadows Juste un rappel amical pour rester sur le sujet. N'hésitez pas à utiliser la salle de mithril gitter si vous souhaitez discuter d'autres sujets.

@appsforartists

Peut-être que React.Children.map devrait développer des fragments

Mithril stable fait quelque chose de similaire en interne (c'est-à-dire aplatir les sous-tableaux), mais je m'éloigne de ce modèle pour des raisons de performance (et aussi en raison de certains maux de tête historiques concernant les listes de vnode qui mélangeaient des nœuds à clé et non à clé). Peut-être quelque chose à considérer.

Et utiliser un "pseudo" élément wrapper, comme un commentaire HTML n'est pas une option ? Je pensais que c'était ainsi que les nœuds de texte étaient "résolus" ...

Nous utilisons https://github.com/mwiencek/react-packages en production depuis quelques mois. Il utilise cette approche de wrapper de commentaires, de sorte que les fragments peuvent être imbriqués sans ambiguïté.

@mwiencek Il est possible d'utiliser votre approche sans package de réaction personnalisé ?

@mwiencek les emballages de commentaires sont-ils même nécessaires ? Je ne m'attendrais pas à des choses intelligentes de fragments, si vous déplacez un élément d'un fragment dans un fragment frère ou l'élément racine, il peut simplement être recréé.

Donc, si vous suivez l'arbre vdom dans l'ordre, vous n'avez pas besoin de commentaires, n'est-ce pas ?

Quoi qu'il en soit, votre solution semble être exactement ce qui est nécessaire pour résoudre ce problème, à première vue. 👍

Pas strictement, mais ils ont simplifié la mise en œuvre dans ce cas.

Donc, essentiellement, il n'est actuellement pas possible de créer des listes de description appropriées <dl> avec React ?

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

@KaiStapel Ce problème concerne le retour de plusieurs composants (ou éléments, je suppose) à partir de render() . Tant que votre fonction render ne renvoie qu'un seul élément/composant racine, cela devrait fonctionner.

D'ACCORD:

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

Pas d'accord:

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 Eh bien codé en dur oui, mais vous ne pouvez pas faire :

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

Ce qui est très triste. Il en va de même pour les tables avec des étendues de lignes, car vous ne pouvez pas afficher deux éléments <tr> à la fois :(

Du haut:

"Moi aussi" et "+1" n'ont pas de valeur, pas plus que les cas d'utilisation déjà écrits dans les commentaires (par exemple, nous savons que vous ne pouvez pas mettre des éléments <tr> ou <dd> avec un <div>) .

Étant donné qu'il existe une solution de travail dans https://github.com/mwiencek/react-packages , y a-t-il une chance que cela fasse bientôt partie de React ? Ou attendons-nous le nouveau réconciliateur ?

Étant donné qu'il existe une solution de travail dans https://github.com/mwiencek/react-packages

L'utilisez-vous avec succès dans des projets réels ?

@mwiencek

La mise en œuvre n'est pas vraiment jolie, mais si cela fonctionne pour les gens, je peux soumettre un PR et laisser les développeurs de React le déchirer.

Bien sûr, veuillez envoyer un PR !

À propos de la soumission d'un PR, la dernière fois que j'ai entendu de @spicyj , c'est qu'ils voulaient terminer le nouvel algorithme de base et trouver une solution appropriée avec cela, car les nœuds de commentaires n'ont pas vraiment de sens dans React Native. Je n'ai pas suivi l'état d'avancement de cela, mais je ne pense pas que ces plans aient changé. Je suis content que les gens trouvent les packages utiles en attendant.

Le nouvel algorithme est en préparation et prend en charge les fragments. Cependant, je ne m'attendrais pas à ce qu'il devienne prêt pour la production dans les mois à venir. Je me demande si l'ajout de ceci d'abord à React DOM et plus tard à React Native est trop mauvais ? L'inconvénient est qu'il fragmente un peu l'écosystème (jeu de mots !), mais cela peut nous donner un peu de temps pour expérimenter cette fonctionnalité. Nous avons une réunion d'équipe aujourd'hui, donc je soulèverai cette question si nous avons le temps de mieux comprendre.

@gaearon Puis-je simplement souligner que le support des fragments est super simple, c'est juste du sucre, j'utilise actuellement <frag> moi-même au moyen d'un petit emballage trivial. Le retour de plusieurs enfants en tant que racine de composant est-il vraiment si important ?

@syranide J'utilise la version personnalisée avec frag dans l'environnement bêta, mais j'aimerais utiliser la version officielle de React à la place. Pouvez-vous fournir votre emballage <frag> ? :) Merci

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

Nous en avons parlé lors de la dernière réunion d'équipe. Le consensus est que nous ne voulons pas aller avec cette mise en œuvre particulière. Cependant, cette fonctionnalité sera prise en charge dans la réécriture du noyau à long terme (pas de calendrier à ce sujet pour le moment).

Nous le considérerons également à nouveau comme l'une des choses sur lesquelles nous pourrions potentiellement travailler pour le second semestre de cette année si la réécriture prend trop de temps ou ne fonctionne pas. Aucune garantie qu'il figurera sur la liste, mais nous vous tiendrons au courant si cela se produit.

Pour avoir une meilleure idée de ce sur quoi nous travaillons, veuillez consulter notre référentiel de notes de réunion ! Vous pouvez trouver notre dernière discussion à ce sujet sur https://github.com/reactjs/core-notes/blob/master/2016-07/july-07.md.

@gaearon Ce serait intéressant d'avoir au moins une syntaxe de frag officielle.

@syranide Merci pour le code, mais malheureusement, il semble que je ne puisse pas l'utiliser car j'ai besoin de frag comme composant d'application racine qui est rendu par la méthode ReactDOM.render et cette méthode n'acceptera pas de fragment .

Merci quand même, cela sera utile pour d'autres personnes qui n'ont pas besoin de frag comme racine de l'application.

@amertak Oui, c'est uniquement pour activer une syntaxe plus raisonnable pour créer des fragments, cela n'ajoute aucune nouvelle fonctionnalité.

@syranide
Je pensais s'il serait possible de rendre le commentaire manuellement et de le traiter comme un autre composant (cela pourrait ne pas être nécessaire) ?
En interne, les commentaires sont traités comme le type #comment donc peut-être être en mesure d'appeler
React.createComponent('#comment', { ... }, children) ?
Juste une idée. Petite solution de contournement.

L'élément clé qui manque ici est de pouvoir rendre le nœud comment , n'est-ce pas ? :)

@gaearon Un peu triste que cela n'arrive pas bientôt, mais j'apprécie la transparence. Bonne rédaction !

Une solution alternative jusqu'à une mise à jour ?
Pour moi, j'ai besoin de rendre un menu déroulant de Botstraap

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

Mais c'est pas possible, une idée ?
Merci,

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

Il est clairement enveloppé dans un seul <div class="btn-group"> comme indiqué dans les Docs

Oui bien sûr, mais il y a deux éléments enveloppés dans <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>

Et deux éléments enveloppés dans un seul élément ne sont pas possibles,
Merci pour votre temps @Primajin

Il suffit de tout déplacer d'un niveau :

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

D'accord, mais j'ai d'autres éléments dans le parent.
Cela ne fera que déplacer le problème.

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

Et dans ce cas, React remplacera tout le contenu.
Est-il possible de faire un "ajout" sans remplacer les autres éléments ?

Merci,

@ rifton007 ce n'est pas comme ça que ça marche. Vous pouvez avoir plusieurs éléments/composants en tant que frères et sœurs, enveloppés dans un conteneur. La restriction est qu'un composant ne peut pas return plusieurs éléments. Le code que vous venez de publier fonctionnera.

Cela étant dit, si vous trouvez un exemple qui ne fonctionne pas, il est poli de lire l'intégralité du fil et de déterminer si vous ajoutez ou non un _nouvel_ exemple ou si vous répétez simplement le même problème qui a déjà été reconnu. Il s'agit d'une restriction connue, et ce fil contient une discussion approfondie des détails et du raisonnement de la restriction. Dan a également déclaré qu'ils avaient l'intention de résoudre ce problème, éventuellement. Qu'essayez-vous d'accomplir en pointant un autre exemple pour un problème reconnu ?

Désolé, je voulais juste savoir si quelqu'un aurait une solution alternative entre-temps.
Vous pouvez supprimer mes messages si nécessaire.

Je crée une application avec Framework7 et React, mais le format html de framework7 est fixe,

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

Je ne peux pas envelopper les éléments enfants de premier niveau (barre de navigation, barre de recherche) dans d'autres div qui n'ont pas la classe 'page'

J'ai un composant qui renvoie une liste de lignes

à utiliser dans un tableau et je ne peux pas les envelopper dans une balise HTML supplémentaire (non autorisé par la norme HTML5 - je connais tbody mais je peux avoir plusieurs lignes renvoyées par plusieurs composants enfants qui peuvent avoir besoin d'être combinés en un seul tbody ). La technique mentionnée par @Prinzhorn - encapsuler ces enfants dans un commentaire HTML - a-t-elle réellement été implémentée par quelqu'un ? J'ai essayé d'implémenter un composant qui ne rend que le commentaire HTML mais cela ne semble pas fonctionner.

Pour info, la réécriture sur laquelle nous travaillons (#6170) supporte déjà les fragments. Vous pouvez suivre nos progrès dans #7925 et sur http://isfiberreadyyet.com.

Le simple fait que l'élément <table> ne puisse pas afficher certains éléments est une raison suffisante pour ajouter cette fonctionnalité afin d'être compatible avec les API Web.

EDIT : Ah, des fragments ! Dans l'attente d'eux.

@trusktr

Comme indiqué dans le tout premier post de ce fil :

Note des mainteneurs :

Nous savons qu'il s'agit d'un problème et nous savons exactement quel ensemble de problèmes peut être résolu . Nous voulons cela aussi, mais c'est un problème difficile avec notre architecture actuelle. Les commentaires supplémentaires exprimant le désir de cette fonctionnalité ne sont pas utiles.

😉

Bon sang, je dois arrêter de faire ça.

J'ai voté pour cela, mais je voulais partager un cas d'utilisation valide. Supposons que j'ai 3 boutons dont je veux une mise en page liquide CSS (gauche, centre, droite). Le bouton de gauche est toujours visible, mais les deux autres sont conditionnellement visibles. Dans React (en particulier React Native), j'utilise sa flexbox et je la rends comme suit :

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

Les boutons central et droit ne se positionnent pas correctement car ils sont enveloppés dans une seule vue.

Oui, je pourrais diviser les boutons central et droit selon leurs propres méthodes, mais cela introduirait beaucoup de code redondant car ils partagent beaucoup de comportement.

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

Comme indiqué ci-dessus, nous connaissons déjà les cas d'utilisation de cette fonctionnalité et travaillons d'arrache-pied pour la prendre en charge. (Il y a déjà une demi-douzaine de personnes ci-dessus qui appellent le problème de la flexbox.) Puisqu'il ne semble pas que plus de discussion sera productive, je verrouille cela. Nous mettrons à jour lorsque nous aurons des nouvelles sur la nouvelle implémentation.

Je pense qu'on peut fermer ça.

Le retour de tableaux à partir de composants est pris en charge depuis React 16 Beta 1 que vous pouvez essayer maintenant .

Il y a encore quelques limitations (le support SSR n'est pas prêt) mais nous les suivons dans # 8854 et nous les corrigerons avant la version finale 16.

Merci à tous pour vos commentaires !

MERCI DANE

🍾🍾🍾

🦏

@gaearon Superbe amélioration ! Prend-il en charge le nœud de texte pur ?

Oui, il prend également en charge les chaînes de retour.

Puis-je demander comment cela est censé fonctionner ?

J'espérais que cela nous permettrait de rendre 2 éléments XJS adjacents mais si je fais return ["foo", "bar"] (quelque chose de plus utile ;), j'obtiens le bundle.js:66656 Warning: Each child in an array or iterator should have a unique "key" prop. attendu

Alors la fonctionnalité était-elle un moyen de ne pas entourer une vraie liste par un élément étranger ?

Alors la fonctionnalité était-elle un moyen de ne pas entourer une vraie liste par un élément étranger ?

Oui, un moyen de fournir plusieurs éléments à partir d'un rendu. (Pour plus d'informations, consultez le message initial sur le problème.)

Si tous les éléments de votre tableau sont connus pour être des chaînes, joignez-les simplement et renvoyez une seule chaîne. Si ce n'est pas le cas, vous pouvez soit manger cet avertissement, soit déterminer ce qu'il faut faire pour les envelopper dans des éléments afin qu'ils puissent être codés pour améliorer la réconciliation DOM, comme vous l'auriez fait si vous aviez inclus un tableau de chaînes dans un autre élément de JSX.

@diligiant renvoyant ['foo', 'bar'] n'est pas pertinent pour ce problème. Vous n'avez jamais pu et ne pouvez toujours pas faire return <div>{['foo','bar']}</div> Chaque enfant dans un tableau doit avoir un prop 'key', qu'il se trouve dans une balise jsx interne comme <div> ou qu'il soit renvoyé. Ce que vous pouvez faire maintenant, c'est :

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

Cela supprime une grande limitation dans la réaction.

Btw retournant ['foo', 'bar'] vous donne un avertissement et non une erreur et pour supprimer cet avertissement, vous pouvez facilement joindre ces chaînes ou s'il s'agit de balises jsx et non de chaînes, vous pouvez leur ajouter un accessoire clé. Il n'y a donc aucune limitation quant au retour de tableaux.

@JesperTreetop merci.
@sassanh , d'après votre exemple, il semble que j'ai été trop "timide" pour utiliser des touches "juste" pour éviter un environnement (sémantiquement) inutile <div> . Voulez-vous dire que je devrais continuer et qu'il n'y a pas de véritable pénalité de performance ? Ce serait en effet une VRAIE amélioration !

@diligiant Je doute que cela introduit une pénalité de performance, mais vous n'avez pas besoin d'un div environnant inutile à moins qu'il ne s'agisse de chaînes environnantes et s'il s'agit de chaînes environnantes, vous pouvez éviter tout tableau et joindre des chaînes. S'il ne s'agit pas de chaînes, vous pouvez ajouter la clé au composant. Par exemple si vous avez deux composants Foo et Bar vous pouvez retourner [<Foo key='foo'/>, <Bar key='bar'/>] et vous n'avez pas besoin d'entourer ni vos composants (comme toujours) ni votre tableau (grâce à cette nouvelle version, vous deviez entourer le tableau avant 16).

@sassanh cool alors. Bien sûr, mon cas d'utilisation était avec plusieurs composants. Content!! ;)

En fait, il me semble étrange que nous avertissions des clés manquantes lorsque tous les éléments sont des chaînes. Je ne m'attendrais pas à ce que nous le fassions. Ce comportement existe-t-il dans 15 lorsque les chaînes sont à l'intérieur d'un div ? Sinon, nous devrions le corriger en 16 pour les chaînes de niveau supérieur (puisqu'il est impossible de leur donner des clés).

@gaearon désolé, mon exemple était trompeur : je voulais dire composants.

J'ai vérifié et sous -beta.5 , React n'émet pas d'avertissement lors du rendu d'un tableau avec plusieurs chaînes.

Néanmoins, je suis intrigué par l'exigence key lors du rendu d'un tableau juste pour éviter un composant environnant. Je comprends et ce n'est rien mais cela soulèvera probablement des tonnes de questions sur SO (je n'ai rien de mieux à proposer cependant…)

Et enfin, merci encore.

Cet avertissement est exactement le même que pour toute autre utilisation de tableaux de composants, car il se résume exactement au même "travail supplémentaire" pour le réconciliateur, et donc exactement à la même optimisation potentielle en définissant un key . Pour moi, la surprise serait que les tableaux de composants soient traités différemment en fonction de cela, et j'imagine que cela susciterait également des questions sur Stack Overflow. Sans oublier que je pense que cela impliquerait des frais généraux juste pour en garder une trace en premier lieu.

.. et la plupart du temps, je renverrai un tableau à partir d'une fonction, je veux que l'avertissement key en place. Il semble que les moments où vous ne voudriez pas que l'avertissement soit la minorité, et il n'y a aucun moyen pour React de savoir s'il est approprié de ne pas avertir.

L'avertissement est nécessaire car le manque de clés peut entraîner des problèmes d'exactitude, pas seulement des problèmes de performances. Ceci est expliqué dans de nombreux autres problèmes demandant pourquoi les clés sont nécessaires, je vous encourage donc à les rechercher et à lire ces discussions. Je suis d'accord que la documentation pourrait être plus claire à ce sujet, et c'est quelque chose que nous examinerons probablement la prochaine fois que nous ferons un sprint de modification de la documentation.

Il n'y a pas de différences conceptuelles entre les tableaux directement renvoyés par le rendu et les tableaux à l'intérieur d'un div. Il n'y a donc aucune raison pour qu'il y ait un avertissement clé dans un cas mais pas dans l'autre. Ils doivent fonctionner de la même manière car les deux sont affectés par les mêmes problèmes lorsque les clés sont manquantes.

Cela dit, nous comprenons qu'il est ennuyeux de spécifier des clés pour un contenu statique . Tout comme vous ne spécifiez pas de clés lorsque vous écrivez une structure JSX où les enfants sont connus de manière statique (et donc jamais réorganisés), il serait bien d'avoir un moyen de le faire avec des tableaux.

À l'avenir, nous pourrions résoudre ce problème en ajoutant une prise en charge explicite des fragments dans JSX avec une syntaxe telle que :

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

Il pourrait produire un tableau mais attribuer implicitement des index numériques aux enfants car dans ce cas, nous savons qu'ils ne peuvent jamais être réorganisés. Cette garantie donnée par l'expression enfant JSX est exactement ce qui nous permet de nous en sortir sans spécifier de clés dans le code JSX normal où un div peut avoir plusieurs enfants.

Mais nous n'avons pas encore cette syntaxe. Donc pour l'instant c'est une limitation connue.

@JesperTreetop & @zwily , @gaearon l'a bien mieux expliqué que moi ;)

Une fois que vous savez, ce n'est pas grave mais comme nous voulons tous que React prospère, je disais juste…

@gaearon Existe-t-il un autre problème pour la proposition de syntaxe <> que nous pouvons regarder au lieu de poursuivre la discussion sur ce problème ? J'ai cherché un peu partout mais je n'ai pas réussi à en trouver un.

@smrq +1 à la question - Je suis tout ce qui concerne ces limitations gênantes (résultat un à un rendrer() , clés et syntaxe ou fragments JSX) mais le seul ticket que je connaisse est https://github.com/ facebook/jsx/problèmes/65

Je suppose également que les fibres vont résoudre le problème avec les clés - mais on dirait que c'est un rêve non réalisé

Les clés ne sont pas un "problème". :) Ils sont une partie fondamentale et nécessaire de tout cadre de vue qui vous permet de créer des listes dynamiques.

Voir https://facebook.github.io/react/tutorial/tutorial.html#keys pour une explication plus détaillée.

@spicyj en fait, c'est un «problème» car cela entraîne des dépenses énergétiques prolongées de la part des développeurs et il existe une possibilité fondamentale de programmer le cadre de vue sans une telle exigence (par exemple https://github.com/dfilatov/vidom)

il existe une possibilité fondamentale de programmer le cadre de vue sans une telle exigence (par exemple https://github.com/dfilatov/vidom)

vidom utilise cependant des clés dans les collections. Cela pourrait techniquement fonctionner sans cela, mais ce serait probablement beaucoup plus lent. React pourrait aussi techniquement fonctionner sans clés, mais il serait assez inattendu de constater que la moitié de vos composants doivent être mis à jour lorsque vous supprimez un seul élément d'une liste. Avec les clés, un seul élément est démonté.

@goto-bus-stop vidom peut utiliser des clés, mais elles ne sont pas obligatoires et sans elles, seuls les très gros cas avec beaucoup de mises à jour peuvent causer de réels problèmes de performances

donc je considère cette partie comme optionnelle possible (comme, par exemple, shouldComponentUpdate ) qui peut être utilisée pour des ajustements de performance dans des cas individuels

@veged exemple de vidom aux prises avec des clés .

Il n'a aucune idée qu'il est censé réorganiser les éléments, il supprime donc les instances à chaque rendu du composant racine.

comme quelqu'un qui est assez familier avec l'espace virtuel-dom [1]. je peux dire que:

  1. les clés sont nécessaires pour des mises à jour prévisibles de dom et d'état parmi des frères et sœurs similaires.
  2. les clés ne sont généralement pas une optimisation et - en fait - sont généralement le contraire.

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

cependant, le problème sans ambiguïté ici (comme @gaearon l'a décrit) est une utilisation entièrement statique des tableaux et la différence entre les tableaux statiques et les fragments JSX statiques

@brigand O n'a aucun doute que la réorganisation des composants à état complet peut causer des problèmes ;-) mais oblige tous les autres cas (à mon avis, plus d'entre eux) à lutter pour cela ... semble controversé

Les clés sont importantes pour React. Même s'ils ne semblent pas avoir d'importance dans certains cas (par exemple, parce que les composants ci-dessous sont tous sans état), rien ne vous empêche, vous ou quelqu'un d'autre dans l'équipe, d'ajouter un composant avec état (ou même une entrée DOM simple) quelque part quelques niveaux en dessous dans quelques mois. À ce moment-là, vous risquez d'oublier que vous n'avez pas de clés et, par conséquent, les réorganisations peuvent entraîner l'association de l'état (ou de la valeur d'entrée) à un élément erroné.

C'est pourquoi nous vous encourageons à spécifier des clés partout pour les listes dynamiques. Ne pas le faire conduit à des bogues très difficiles à tracer, et nous pensons qu'il vaut mieux passer dix secondes supplémentaires à spécifier la clé que dix heures à déboguer pourquoi l'état d'un composant plusieurs niveaux en dessous est gâché dans certains cas particuliers.

Je suis entièrement d'accord qu'il n'est pas pratique de spécifier des clés lorsque la liste est connue pour être statique et ne se réorganise jamais. Vous êtes invités à en discuter sur le référentiel JSX. Si vous ne trouvez pas de problème pour cela, vous pouvez en créer un nouveau ici.

Étant donné que ce fil a beaucoup d'abonnés et que la fonctionnalité a été implémentée, je voudrais le verrouiller pour éviter de spammer beaucoup de personnes avec des notifications. J'espère que les clarifications ci-dessus répondent à vos préoccupations, mais si ce n'est pas le cas, vous pouvez créer un nouveau sujet pour approfondir la discussion sur un sujet spécifique.

@smrq a créé un problème de proposition pour la syntaxe <> sur le référentiel jsx : https://github.com/facebook/jsx/issues/84.

Nous venons de publier la prise en charge d'une nouvelle exportation React.Fragment et de la syntaxe associée <> :
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

Cette page vous a été utile?
0 / 5 - 0 notes