React: RFC : planifier les attributs/propriétés des éléments personnalisés dans React 18

Créé le 24 oct. 2017  ·  129Commentaires  ·  Source: facebook/react

Ceci est destiné à l'adresse #7249. Le document décrit les avantages et les inconvénients des différentes approches que React pourrait utiliser pour gérer les attributs et les propriétés des éléments personnalisés.

Table des matières/Résumé

  • Fond
  • Les propositions

    • Option 1 : définir uniquement les propriétés

    • Avantages



      • Facile à comprendre/mettre en œuvre


      • Évite les conflits avec les futurs attributs globaux


      • Tire parti de l'élément personnalisé "mise à niveau"


      • Éléments personnalisés traités comme tout autre composant React



    • Les inconvénients



      • Peut-être un changement décisif


      • Besoin d'une référence pour définir l'attribut


      • Pas clair comment le rendu côté serveur fonctionnerait



    • Option 2 : Propriétés si disponibles

    • Avantages



      • Changement sans rupture



    • Les inconvénients



      • Les développeurs doivent comprendre l'heuristique


      • Revenir aux attributs peut entrer en conflit avec les futurs globaux



    • Option 3 : Différencier les propriétés avec un sceau

    • Avantages



      • Changement ininterrompu auquel les développeurs peuvent adhérer


      • Similaire à la façon dont les autres bibliothèques gèrent les attributs/propriétés


      • Le système est explicite



    • Les inconvénients



      • C'est une nouvelle syntaxe


      • Pas clair comment le rendu côté serveur fonctionnerait



    • Option 4 : Ajouter un objet d'attributs

    • Avantages



      • Le système est explicite


      • L'extension de la syntaxe peut également résoudre les problèmes de gestion des événements



    • Les inconvénients



      • C'est une nouvelle syntaxe


      • C'est peut-être un changement décisif


      • Il peut s'agir d'un changement plus important que n'importe laquelle des propositions précédentes



    • Option 5 : Une API pour consommer des éléments personnalisés

    • Avantages



      • Le système est explicite


      • Changement sans rupture


      • Idiomatique pour réagir



    • Les inconvénients



      • Cela peut représenter beaucoup de travail pour un composant complexe


      • Peut gonfler la taille du paquet


      • La configuration doit suivre le rythme du composant



Fond

Lorsque React essaie de transmettre des données à un élément personnalisé, il le fait toujours en utilisant des attributs HTML.

<x-foo bar={baz}> // same as setAttribute('bar', baz)

Étant donné que les attributs doivent être sérialisés en chaînes, cette approche crée des problèmes lorsque les données transmises sont un objet ou un tableau. Dans ce scénario, nous nous retrouvons avec quelque chose comme :

<x-foo bar="[object Object]">

La solution de contournement consiste à utiliser un ref pour définir manuellement la propriété.

<x-foo ref={el => el.bar = baz}>

Cette solution de contournement semble un peu inutile car la majorité des éléments personnalisés livrés aujourd'hui sont écrits avec des bibliothèques qui génèrent automatiquement des propriétés JavaScript qui soutiennent tous leurs attributs exposés. Et toute personne écrivant à la main un élément personnalisé vanille est également encouragée à suivre cette pratique . Nous aimerions idéalement voir la communication d'exécution avec des éléments personnalisés dans React utiliser les propriétés JavaScript par défaut.

Ce document présente quelques propositions sur la façon dont React pourrait être mis à jour pour que cela se produise.

Les propositions

Option 1 : définir uniquement les propriétés

Plutôt que d'essayer de décider si une propriété ou un attribut doit être défini, React pourrait toujours définir des propriétés sur des éléments personnalisés. React ne vérifierait

Exemple:

<x-foo bar={baz}>

Le code ci - dessus entraînerait la mise React le .bar propriété de x-foo élément égal à la valeur de baz .

Pour les noms de propriété camelCased, React pourrait utiliser le même style qu'il utilise aujourd'hui pour des propriétés telles que tabIndex .

<x-foo squidInk={pasta}> // sets .squidInk = pasta

Avantages

Facile à comprendre/mettre en œuvre

Ce modèle est simple, explicite et s'harmonise avec l' "API JavaScript-centric vers le DOM" de React.

Tout élément créé avec des bibliothèques telles que Polymer ou Skate générera automatiquement des propriétés pour sauvegarder leurs attributs exposés. Ces éléments devraient tous « fonctionner » avec l'approche ci-dessus. Les développeurs qui créent à la main des composants vanille sont encouragés à sauvegarder les attributs avec des propriétés car cela reflète la façon dont les éléments HTML5 <input> ) ( <video> , <audio> , etc.) ont été mis en œuvre.

Évite les conflits avec les futurs attributs globaux

Lorsque React définit un attribut sur un élément personnalisé, il y a toujours le risque qu'une future version de HTML embarque un attribut du même nom et casse des choses. Cette préoccupation a été discutée avec les auteurs des spécifications, mais il n'y a pas de solution claire au problème. Éviter complètement les attributs (sauf lorsqu'un développeur en définit explicitement un en utilisant ref ) peut éviter ce problème jusqu'à ce que les navigateurs proposent une meilleure solution.

Tire parti de l'élément personnalisé "mise à niveau"

Les éléments personnalisés peuvent être mis à

Éléments personnalisés traités comme tout autre composant React

Lorsque les composants React se transmettent des données, ils utilisent déjà des propriétés. Cela ferait simplement en sorte que les éléments personnalisés se comportent de la même manière.

Les inconvénients

Peut-être un changement décisif

Si un développeur a créé à la main des éléments personnalisés vanilla qui n'ont qu'une API d'attributs, il devra alors mettre à jour son code ou son application se brisera. Le correctif serait d'utiliser un ref pour définir l'attribut (expliqué ci-dessous).

Besoin d'une référence pour définir l'attribut

En modifiant le comportement afin que les propriétés soient préférées, cela signifie que les développeurs devront utiliser un ref afin de définir explicitement un attribut sur un élément personnalisé.

<custom-element ref={el => el.setAttribute('my-attr', val)} />

Ceci est juste un renversement du comportement actuel où les développeurs ont besoin d'un ref afin de définir une propriété. Étant donné que les développeurs devraient rarement avoir besoin de définir des attributs sur des éléments personnalisés, cela semble être un compromis raisonnable.

Pas clair comment le rendu côté serveur fonctionnerait

Il n'est pas clair comment ce modèle serait mappé aux éléments personnalisés de rendu côté serveur. React pourrait supposer que les propriétés correspondent à des attributs portant le même nom et tenter de les définir sur le serveur, mais cela est loin d'être à l'épreuve des balles et nécessiterait peut-être une heuristique pour des choses comme les propriétés camelCased -> les attributs en tiret.

Option 2 : Propriétés si disponibles

Au moment de l'exécution, React pourrait tenter de détecter si une propriété est présente sur un élément personnalisé. Si la propriété est présente, React l'utilisera, sinon il reviendra à la définition d'un attribut. C'est le modèle que Preact utilise pour gérer les éléments personnalisés.

Implémentation du pseudocode :

if (propName in element) {
  element[propName] = value;
} else {
  element.setAttribute(propName.toLowerCase(), value);
}

Étapes possibles :

  • Si un élément a une propriété définie, React l'utilisera.

  • Si un élément a une propriété non définie et que React essaie de lui transmettre des données primitives (chaîne/numéro/booléen), il utilisera un attribut.

    • Alternative : Avertir et ne pas définir.
  • Si un élément a une propriété indéfinie et que React essaie de lui transmettre un objet/tableau, il le définira en tant que propriété. C'est parce que some-attr="[object Object]" n'est pas utile.

    • Alternative : Avertir et ne pas définir.
  • Si l'élément est rendu sur le serveur et que React essaie de lui transmettre une chaîne/un nombre/un booléen, il utilisera un attribut.

  • Si l'élément est rendu sur le serveur et que React essaie de lui passer un objet/tableau, il ne fera rien.

Avantages

Changement sans rupture

Il est possible de créer un élément personnalisé qui utilise uniquement des attributs comme interface. Ce style de création n'est PAS encouragé, mais cela peut arriver quand même. Si un auteur d'élément personnalisé s'appuie sur ce comportement, ce changement serait ininterrompu pour lui.

Les inconvénients

Les développeurs doivent comprendre l'heuristique

Les développeurs peuvent être confus lorsque React définit un attribut au lieu d'une propriété en fonction de la façon dont ils ont choisi de charger leur élément.

Revenir aux attributs peut entrer en conflit avec les futurs globaux

Sebastian a soulevé une inquiétude selon laquelle l'utilisation de in pour vérifier l'existence d'une propriété sur un élément personnalisé pourrait accidentellement détecter une propriété sur la superclasse (HTMLElement).

Il existe également d'autres conflits potentiels avec les attributs globaux décrits précédemment dans ce document.

Option 3 : Différencier les propriétés avec un sceau

React pourrait continuer à définir des attributs sur des éléments personnalisés, mais fournir un sceau que les développeurs pourraient utiliser pour définir explicitement les propriétés à la place. Ceci est similaire à l'approche utilisée par Glimmer.js .

Exemple de lueur :

<custom-img @src="corgi.jpg" @hiResSrc="[email protected]" width="100%">

Dans l'exemple ci-dessus, le sigil @ indique que src et hiResSrc doivent transmettre des données à l'élément personnalisé à l'aide de propriétés, et width doit être sérialisé en une chaîne d'attribut.

Étant donné que les composants React se transmettent déjà des données à l'aide de propriétés, ils n'auraient pas besoin d'utiliser le sceau (bien que cela fonctionnerait s'ils le faisaient, ce serait simplement redondant). Au lieu de cela, il serait principalement utilisé comme une instruction explicite pour transmettre des données à un élément personnalisé à l'aide des propriétés JavaScript.

h/t à @developit de Preact pour avoir suggéré cette approche :)

Avantages

Changement ininterrompu auquel les développeurs peuvent adhérer

Toutes les applications d'éléments personnalisés React + préexistantes continueraient à fonctionner exactement comme elles l'ont fait. Les développeurs pouvaient choisir s'ils voulaient mettre à jour leur code pour utiliser le nouveau style de sigil.

Similaire à la façon dont les autres bibliothèques gèrent les attributs/propriétés

Semblable à Glimmer, Angular et Vue utilisent des modificateurs pour différencier les attributs et les propriétés.

Exemple de vue :

<!-- Vue will serialize `foo` to an attribute string, and set `squid` using a JavaScript property -->
<custom-element :foo="bar” :squid.prop=”ink”>

Exemple angulaire :

<!-- Angular will serialize `foo` to an attribute string, and set `squid` using a JavaScript property -->
<custom-element [attr.foo]="bar” [squid]=”ink”>

Le système est explicite

Les développeurs peuvent dire à React exactement ce qu'ils veulent au lieu de s'appuyer sur une heuristique comme l'approche des propriétés si disponibles .

Les inconvénients

C'est une nouvelle syntaxe

Les développeurs doivent apprendre à l'utiliser et il doit être soigneusement testé pour s'assurer qu'il est rétrocompatible.

Pas clair comment le rendu côté serveur fonctionnerait

Le sceau doit-il utiliser un attribut du même nom ?

Option 4 : Ajouter un objet d'attributs

React pourrait ajouter une syntaxe supplémentaire qui permet aux auteurs de transmettre explicitement des données en tant qu'attributs. Si les développeurs n'utilisent pas cet objet d'attributs, leurs données seront transmises à l'aide des propriétés JavaScript.

Exemple:

const bar = 'baz';
const hello = 'World';
const width = '100%';
const ReactElement = <Test
  foo={bar} // uses JavaScript property
  attrs={{ hello, width }} // serialized to attributes
/>;

Cette idée a été initialement proposée par @treshugart , auteur de Skate.js, et est implémentée dans la bibliothèque val .

Avantages

Le système est explicite

Les développeurs peuvent dire à React exactement ce qu'ils veulent au lieu de s'appuyer sur une heuristique comme l'approche des propriétés si disponibles .

L'extension de la syntaxe peut également résoudre les problèmes de gestion des événements

Remarque : Cela sort du cadre de ce document, mais cela vaut peut-être la peine d'être mentionné :)

Le problème #7901 demande à React de contourner son système d'événements synthétiques lorsque des gestionnaires d'événements déclaratifs sont ajoutés aux éléments personnalisés. Étant donné que les noms d'événements d'éléments personnalisés sont des chaînes arbitraires, cela signifie qu'ils peuvent être mis en majuscule de n'importe quelle manière. Pour contourner le système d'événements synthétiques aujourd'hui, il faudra également proposer une heuristique pour mapper les noms d'événements de JSX à addEventListener .

// should this listen for: 'foobar', 'FooBar', or 'fooBar'?
onFooBar={handleFooBar}

Cependant, si la syntaxe est étendue pour autoriser les attributs, elle peut également être étendue pour autoriser les événements :

const bar = 'baz';
const hello = 'World';
const SquidChanged = e => console.log('yo');
const ReactElement = <Test
  foo={bar}
  attrs={{ hello }}
  events={{ SquidChanged}} // addEventListener('SquidChanged', …)
/>;

Dans ce modèle, le nom de la variable est utilisé comme nom d'événement. Aucune heuristique n'est nécessaire.

Les inconvénients

C'est une nouvelle syntaxe

Les développeurs doivent apprendre à l'utiliser et il doit être soigneusement testé pour s'assurer qu'il est rétrocompatible.

C'est peut-être un changement décisif

Si des composants reposent déjà sur des propriétés nommées attrs ou events , cela pourrait les casser.

Il peut s'agir d'un changement plus important que n'importe laquelle des propositions précédentes

Pour React 17, il peut être plus facile d'apporter un changement incrémentiel (comme l'une des propositions précédentes) et de positionner cette proposition comme quelque chose à prendre en considération pour un remaniement ultérieur plus important.

Option 5 : Une API pour consommer des éléments personnalisés

Cette proposition a été offerte par @sophiebits et @gaearon de l'équipe React

React pourrait créer une nouvelle API pour consommer des éléments personnalisés qui mappe le comportement de l'élément avec un objet de configuration.

Exemple de pseudocode :

const XFoo = ReactDOM.createCustomElementType({
  element: ‘x-foo’,
  ‘my-attr’: // something that tells React what to do with it
  someRichDataProp: // something that tells React what to do with it
});

Le code ci-dessus renvoie un composant proxy, XFoo qui sait comment transmettre des données à un élément personnalisé en fonction de la configuration que vous fournissez. Vous utiliseriez ce composant proxy dans votre application au lieu d'utiliser directement l'élément personnalisé.

Exemple d'utilisation :

<XFoo someRichDataProp={...} />

Avantages

Le système est explicite

Les développeurs peuvent indiquer à React le comportement exact qu'ils souhaitent.

Changement sans rupture

Les développeurs peuvent choisir d'utiliser l'objet ou continuer à utiliser le système actuel.

Idiomatique pour réagir

Ce changement ne nécessite pas de nouvelle syntaxe JSX et ressemble plus aux autres API de React. Par exemple, PropTypes (même s'il est déplacé dans son propre package) a une approche quelque peu similaire.

Les inconvénients

Cela peut représenter beaucoup de travail pour un composant complexe

L'élément d' entrée de

Peut gonfler la taille du paquet

En lien avec le point ci-dessus, chaque classe d'élément personnalisé supporte désormais le coût de sa définition + sa taille d'objet de configuration.

Remarque : je ne suis pas sûr à 100 % si cela est vrai.

La configuration doit suivre le rythme du composant

Chaque fois que le composant effectue une révision de version mineure qui ajoute une nouvelle propriété, la configuration devra également être mise à jour. Ce n'est pas difficile, mais cela ajoute de l'entretien. Peut-être que si les configurations sont générées à partir de la source, c'est moins lourd, mais cela peut signifier avoir besoin de créer un nouvel outil pour générer des configurations pour chaque bibliothèque de composants Web.

cc @sebmarkbage @gaearon @developit @treshugart @justinfagnani

DOM Discussion

Commentaire le plus utile

Salut les amis, en attendant, j'ai créé un shim pour envelopper votre composant web dans React https://www.npmjs.com/package/reactify-wc

import React from "react";
import reactifyWc from "reactify-wc";

// Import your web component. This one defines a tag called 'vaadin-button'
import "@vaadin/vaadin-button";

const onClick = () => console.log('hello world');

const VaadinButton = reactifyWc("vaadin-button");

export const MyReactComponent = () => (
  <>
    <h1>Hello world</h1>
    <VaadinButton onClick={onClick}>
      Click me!
    </VaadinButton>
  </>
)

J'espère que cela s'avère utile

(C'est ma première incursion dans OSS, et l'un des premiers open source de quelque chose hors de mon bureau. les critiques constructives sont plus que bienvenues 😄 )

Tous les 129 commentaires

Toutes mes excuses pour la longue lecture, mais je voulais m'assurer que j'explorais à fond chaque option. Je ne veux pas trop biaiser les choses avec ma propre opinion, mais si j'étais en mesure de choisir, je pense que j'irais avec l'option 3.

L'option 3 est rétrocompatible, déclarative et explicite. Il n'est pas nécessaire de maintenir une heuristique de secours, et d'autres bibliothèques fournissent déjà des sigils/modificateurs similaires.

Toutes mes excuses pour la longue lecture, mais je voulais m'assurer que j'explorais à fond chaque option. Je ne veux pas trop biaiser les choses avec ma propre opinion, mais si j'étais en mesure de choisir, je pense que j'irais avec l'option 3.
L'option 3 est rétrocompatible, déclarative et explicite. Il n'est pas nécessaire de maintenir une heuristique de secours, et d'autres bibliothèques fournissent déjà des sigils/modificateurs similaires.

Je suis entre l'option 2 et l'option 3, je pense que React a très bien géré les changements de comportement et d'API dans le passé. L'introduction d'avertissements et de liens vers des documents peut être utile pour aider les développeurs à comprendre ce qui se passe sous le capot.

L'option 3 semble attrayante en raison de sa nature déclarative, tout en lisant le code JSX, les nouveaux développeurs à venir sauront immédiatement ce que React fera lors du rendu de l'élément.

Commentaires sur l'option 2

Les développeurs peuvent être confus lorsque React définit un attribut au lieu d'une propriété en fonction de la façon dont ils ont choisi de charger leur élément.

Les consommateurs d'un élément personnalisé doivent-ils comprendre cette distinction ? Ou est-ce seulement important pour l'auteur de l'élément personnalisé ? Il semble que l'auteur de l'élément devra gérer les attributs de tout ce qui est utilisé en HTML (puisque c'est la seule façon dont les données sont transmises à partir de l'utilisation HTML) et les propriétés s'ils souhaitent prendre en charge des valeurs complexes ou des propriétés get/set de DOM. Il est même possible qu'un auteur ait quelque chose initialement implémenté en tant qu'attribut, puis ajoute ultérieurement une propriété du même nom pour prendre en charge des types de données plus flexibles et toujours sauvegarder la propriété avec une valeur stockée dans les attributs.

Les collisions de noms avec les futurs attributs et propriétés HTMLElement semblent être une faiblesse dans les normes des composants Web en général, car cela peut entraîner des erreurs quelle que soit l'approche de liaison.

Si un élément a une propriété indéfinie et que React essaie de lui transmettre un objet/tableau, il le définira en tant que propriété. C'est parce que some-attr="[object Object]" n'est pas utile.

Il semble déroutant de lier différemment en fonction de la valeur. Si l'auteur de l'élément n'a pas spécifié de propriété getter/setter pour gérer la valeur, la définition de la propriété entraînerait le comportement de l'élément comme si la valeur n'avait jamais été spécifiée, ce qui pourrait être plus difficile à déboguer.

Commentaires sur l'option 3

Un autre inconvénient potentiel de l'option 3 est qu'elle oblige le consommateur de l'élément personnalisé à savoir si l'élément a implémenté quelque chose en tant que propriété ou en tant qu'attribut. Si vous utilisez un mélange de composants React et d'éléments personnalisés, il peut être déroutant de définir des accessoires React en utilisant une syntaxe et des propriétés d'élément personnalisées en utilisant une syntaxe différente.

Les consommateurs d'un élément personnalisé doivent-ils comprendre cette distinction ? Ou est-ce seulement important pour l'auteur de l'élément personnalisé ?

Je doute que ce soit en fait un énorme problème car, comme vous l'avez souligné, l'auteur de l'élément doit définir un attribut et une propriété pour la valeur sous-jacente et accepter les données des deux. J'ajouterais également qu'ils doivent garder l'attribut et la propriété synchronisés (donc définir l'un définit l'autre).

Les collisions de noms avec les futurs attributs et propriétés HTMLElement semblent être une faiblesse dans les normes des composants Web en général, car cela peut entraîner des erreurs quelle que soit l'approche de liaison.

Je suis d'accord mais je ne sais pas si c'est quelque chose que React doit essayer de contourner dans sa bibliothèque. Cela ressemble à un problème qui doit être résolu dans le cadre de la spécification des éléments personnalisés. Je peux voir si nous pouvons en discuter dans le cadre de la prochaine réunion des normes TPAC.

Je devrais ajouter, pour les propriétés, ce n'est pas _comme_ mauvais car la propriété définie par l'élément masquera la future propriété ajoutée à HTMLElement. Donc, si vous passiez des données à un élément personnalisé en tant que propriété js, cela continuerait de fonctionner. Le problème principal semble être autour des attributs puisqu'ils sont globaux.

Il semble déroutant de lier différemment en fonction de la valeur. Si l'auteur de l'élément n'a pas spécifié de propriété getter/setter pour gérer la valeur, la définition de la propriété entraînerait le comportement de l'élément comme si la valeur n'avait jamais été spécifiée, ce qui pourrait être plus difficile à déboguer.

Dans le cas où un élément personnalisé est chargé paresseux et "mis à niveau", il aura initialement des propriétés non définies. Cela résout ce cas d'utilisation en s'assurant que ces éléments reçoivent toujours leurs données et qu'ils peuvent les utiliser après la mise à niveau.

Il est vrai que si l'auteur ne définit pas de getter/setter pour une valeur cela ne serait pas très utile. Mais il n'est pas non plus utile d'avoir un my-attr=[object Object] . Et puisque vous ne savez pas si la propriété est vraiment indéfinie ou si la définition est simplement chargée paresseux, il semble plus sûr de définir la propriété.

Un autre inconvénient potentiel de l'option 3 est qu'elle oblige le consommateur de l'élément personnalisé à savoir si l'élément a implémenté quelque chose en tant que propriété ou en tant qu'attribut.

Je pense que vous êtes essentiellement dans le même bateau aujourd'hui car rien n'oblige un auteur d'élément personnalisé à définir un attribut au lieu d'une propriété. Je pourrais donc avoir un élément avec une API de propriétés uniquement qui ne recevrait aucune donnée du système actuel de React et j'aurais besoin de savoir utiliser ref pour définir directement les propriétés js.

Étant donné que les éléments personnalisés sont conçus comme une primitive, rien n'impose la création d'attributs et de propriétés correspondants. Mais nous nous efforçons d'encourager le fait de le faire en tant que meilleure pratique, et toutes les bibliothèques que je connais aujourd'hui créent des propriétés de support pour leurs attributs.

[Éditer]

Comme vous l'avez indiqué dans votre point précédent :

Il semble que l'auteur de l'élément devra gérer les attributs de tout ce qui est utilisé en HTML (puisque c'est la seule façon dont les données sont transmises à partir de l'utilisation HTML) et les propriétés s'ils souhaitent prendre en charge des valeurs complexes ou des propriétés get/set de DOM.

Parce que vous ne savez jamais comment un utilisateur essaiera de transmettre des données à votre élément, vous aurez de toute façon besoin d'une correspondance attribut-propriété. J'imagine que si l'option 3 était expédiée, la plupart des gens lieraient tout simplement en utilisant le sigil @ parce que ce serait plus simple. C'est ainsi que je travaille avec des éléments personnalisés dans Vue aujourd'hui, car ils exposent un modificateur .prop .

cela nécessite que le consommateur de l'élément personnalisé sache si l'élément a implémenté quelque chose en tant que propriété ou en tant qu'attribut

Ce n'est pas quelque chose que React devrait s'inquiéter comme Rob l'a dit à mon avis, c'est la responsabilité de l'auteur de l'élément personnalisé d'informer l'utilisateur du fonctionnement de l'élément.

Et c'est en fait la façon dont nous devons le faire aujourd'hui, par exemple, pensez à l'élément <video> , disons que vous devez le désactiver ou modifier l'heure actuelle dans un composant.

muted fonctionne comme un attribut booléen

render() {
  return (
    <div className="video--wrapper">
      <video muted={ this.state.muted } />
    </div>
  );
}

Pour le moment, vous devez créer un ref pointant vers l'élément vidéo et modifier la propriété.

render() {
  return (
    <div className="video--wrapper">
      <video ref={ el => this.video = el } muted={ this.state.muted } />
    </div>
  );
}

Créez ensuite un gestionnaire d'événements, une méthode d'instance et définissez manuellement la propriété sur l'élément DOM.

onCurrenTimeChange(e) {
  this.video.currentTime = e.value;
}

Si vous y réfléchissez, cela brise un peu le modèle déclaratif que React lui-même impose avec son API et sa couche abstraite JSX puisque le currentTime c'est clairement un état dans le composant wrapper, avec la liaison de propriété, nous aurions toujours besoin du gestionnaire d'événements mais le Le modèle d'abstraction JSX serait plus déclaratif et les références ne seraient pas nécessaires uniquement pour cela :

render() {
  return (
    <div className="video--wrapper">
      <video muted={ this.state.muted } @currentTime={ this.state.currentTime } />
    </div>
  );
}

Mon point est que, que vous vous appuyiez sur des éléments natifs ou personnalisés, vous devez toujours vous y retrouver en fonction de la documentation, à la différence que dans le second cas, cela devrait provenir de l'auteur de l'élément personnalisé.

@cjorasch mes deux cents :)

Si nous concevions cela à partir de zéro, sans avoir besoin de prendre en compte la compatibilité descendante, je pense que l'option 1 serait la plus idiomatique selon "l'API JavaScript-centric vers le DOM" de React.

En ce qui concerne le rendu côté serveur, ce problème pourrait-il être résolu en fournissant une API pour le code d'application afin d'informer React sur la façon de mapper les propriétés des éléments personnalisés aux attributs ? Similaire aux cartes que React gère déjà pour les attributs définis par la plate-forme ? Cette API n'aurait besoin d'être invoquée qu'une seule fois par nom d'élément personnalisé (pas pour chaque instance de celui-ci), et uniquement pour les propriétés qui ne suivent pas une correspondance 1:1 directe avec leur attribut, ce qui devrait, espérons-le, être relativement rare.

Si nous craignons que ce changement soit trop important, alors je pense que l'option 3 est également très attrayante. Si le sigil signifie une propriété, je suggérerais ".", car c'est déjà l'accesseur de propriété de JavaScript. Cependant, je pense qu'il est malheureux de faire en sorte que chaque instance d'utilisation d'un élément personnalisé dans une application soit responsable de savoir quand utiliser un attribut et quand utiliser une propriété. Ce que je préfère à propos de l'option 1, c'est que même si une propriété à attribuer à la carte est nécessaire, ce code de mappage peut être isolé de toutes les utilisations de JSX.

Dans le cas où un élément personnalisé est chargé paresseux et "mis à niveau", il aura initialement des propriétés non définies. Cela résout ce cas d'utilisation en s'assurant que ces éléments reçoivent toujours leurs données et qu'ils peuvent les utiliser après la mise à niveau.

Peut-être que je ne comprends pas le processus de mise à niveau. Les éléments auraient généralement des propriétés définies en tant que getters/setters dans le prototype de classe. La vérification de propName in element renverrait true en raison de l'existence du getter/setter même si la valeur de la propriété n'était toujours pas définie. Au cours de la mise à niveau, les valeurs de propriété sont-elles définies sur une instance temporaire, puis copiées ultérieurement sur l'instance réelle une fois le chargement différé terminé ?

La mise à niveau est le processus par lequel l'élément personnalisé reçoit sa classe. Avant cela, ce n'est pas une instance de cette classe, donc les getters/setters de propriété ne sont pas disponibles.

@jeremenichelli

muted fonctionne comme un attribut booléen

juste vérifié et il a également une propriété correspondante bien qu'il ne semble pas être documenté sur MDN :P

Pour le moment, vous devez créer une référence pointant vers l'élément vidéo et modifier la propriété.

Oui, parfois, vous rencontrerez des API de propriétés uniquement sur des éléments HTML modernes. currentTime met à jour à une fréquence élevée, il n'aurait donc pas de sens de le refléter dans un attribut HTML.

Mon point est que, que vous vous appuyiez sur des éléments natifs ou personnalisés, vous devez toujours vous y retrouver en vous basant sur la documentation.

Oui, il n'y a malheureusement pas de règle unique d'attributs/propriétés. Mais je pense que de manière générale, vous pouvez vous appuyer fortement sur les propriétés et fournir une syntaxe afin que les développeurs puissent utiliser des attributs dans des cas particuliers.

@robdodson oui, je connaissais aussi la propriété muet 😄 Je viens d'utiliser ces deux pour prouver que déjà _dans la nature_ il n'y a pas de règle unique comme vous l'avez mentionné.

Nous devrons nous appuyer sur la documentation des éléments natifs et personnalisés, c'est donc quelque chose qui ne me dérangerait pas pour cette décision.

Lors de l'écriture du dernier extrait de code, j'ai bien aimé la liaison de propriété 💟

@effulgentsia

Cependant, je pense qu'il est malheureux de faire en sorte que chaque instance d'utilisation d'un élément personnalisé dans une application soit responsable de savoir quand utiliser un attribut et quand utiliser une propriété.

Je pense que c'est déjà le cas aujourd'hui. Étant donné que les principales bibliothèques d'éléments personnalisés (polymère, skate, peut-être d'autres ?) créent automatiquement des propriétés de support pour tous les attributs exposés, les développeurs peuvent simplement utiliser le sceau pour chaque propriété d'un élément personnalisé. Il serait probablement rare qu'ils aient besoin de passer à l'utilisation d'un attribut.

@cjorasch

RE : mise à niveau. Comme @effulgentsia l'a mentionné, il est possible d'avoir un élément personnalisé sur la page mais de charger sa définition ultérieurement. <x-foo> sera initialement une instance de HTMLElement et lorsque je charge sa définition plus tard, elle "se met à niveau" et devient une instance de la classe XFoo . À ce stade, tous ses rappels de cycle de vie sont exécutés. Nous utilisons cette technique dans le projet Polymer Starter Kit. Un peu comme ça :

<app-router>
  <my-view1></my-view1>
  <my-view2></my-view2>
</app-router>

Dans l'exemple ci-dessus, nous ne chargerons pas la définition de my-view2 jusqu'à ce que le routeur la modifie.

Il est tout à fait possible de définir une propriété sur l'élément avant sa mise à niveau, et une fois la définition chargée, l'élément peut récupérer ces données lors de l'un de ses rappels de cycle de vie.

les développeurs pourraient simplement utiliser le sceau pour chaque propriété sur un élément personnalisé

Si les développeurs commençaient à faire cela, alors comment cela différencierait-il l'utilisation d'une propriété parce que vous « pouvez » d'utiliser une propriété parce que vous « devez » ? Et n'est-ce pas une différenciation nécessaire pour le rendu côté serveur ?

Si les développeurs commençaient à faire cela, alors comment cela différencierait-il l'utilisation d'une propriété parce que vous « pouvez » d'utiliser une propriété parce que vous « devez » ?

Désolé, peut-être que je me suis mal exprimé. Je voulais dire que les développeurs utiliseraient probablement le sceau car il donnerait le résultat le plus cohérent. Vous pouvez l'utiliser pour transmettre des données primitives ou des données riches comme des objets et des tableaux et cela fonctionnera toujours. Je pense qu'il est généralement préférable de travailler avec des propriétés au moment de l'exécution plutôt que de travailler avec des attributs, car les attributs ont tendance à être davantage utilisés pour la configuration initiale.

Et n'est-ce pas une différenciation nécessaire pour le rendu côté serveur ?

Il se peut que sur le serveur, le sceau se replie sur la définition d'un attribut.

Il se peut que sur le serveur, le sceau se replie sur la définition d'un attribut.

Je ne pense pas que cela fonctionnerait si la raison du sceau est qu'il s'agit d'une propriété qui n'existe pas en tant qu'attribut, comme le currentTime de la vidéo.

différencier l'utilisation d'une propriété parce que vous « pouvez » d'utiliser une propriété parce que vous « devez »

Je pense que cette différenciation est importante, car il y a des raisons totalement différentes pour choisir d'utiliser un attribut ou une propriété comme optimisation (par exemple, SSR préférant les attributs contre le rendu côté client préférant les propriétés) par rapport à quelque chose qui n'existe que comme attribut ou une propriété.

En ce qui concerne le rendu côté serveur, ce problème pourrait-il être résolu en fournissant une API pour le code d'application afin d'informer React sur la façon de mapper les propriétés des éléments personnalisés aux attributs ?

Pour être plus précis, je propose quelque chose comme ceci :

ReactDOM.defineCustomElementProp(elementName, propName, domPropertyName, htmlAttributeName, attributeSerializer)

Exemples:

// 'muted' can be set as either a property or an attribute.
ReactDOM.defineCustomElementProp('x-foo', 'muted', 'muted', 'muted')

// 'currentTime' can only be set as a property.
ReactDOM.defineCustomElementProp('x-foo', 'currentTime', 'currentTime', null)

// 'my-attribute' can only be set as an attribute.
ReactDOM.defineCustomElementProp('x-foo', 'my-attribute', null, 'my-attribute')

// 'richData' can be set as either a property or an attribute.
// When setting as an attribute, set it as a JSON string rather than "[object Object]".
ReactDOM.defineCustomElementProp('x-foo', 'richData', 'richData', 'richdata', JSON.stringify)

Pour quelque chose qui ne peut être qu'une propriété (où htmlAttributeName est nul), SSR sauterait le rendu et l'hydraterait ensuite sur le client.

Pour quelque chose qui ne peut être qu'un attribut (où domPropertyName est nul), React invoquerait setAttribute() comme actuellement dans la v16.

Pour quelque chose qui peut être les deux, React pourrait choisir la stratégie la plus optimale. Cela signifie peut-être toujours définir comme propriété côté client, mais comme attribut côté serveur. Cela signifie peut-être définir comme attribut lors de la création initiale de l'élément, mais définir comme propriété lors de la mise à jour ultérieure à partir du vdom. Cela signifie peut-être uniquement définir comme attribut lorsque la valeur est un type primitif. Idéalement, React devrait être en mesure de modifier la stratégie à tout moment en tant que détail d'implémentation interne.

Lorsque React rencontre un accessoire pour lequel defineCustomElementProp() n'a pas été appelé et qui n'est pas défini par la spécification HTML en tant que propriété ou attribut global, alors React peut implémenter une logique par défaut. Par exemple, peut-être :

  • Dans la version 17, maintenez BC avec la v16 et définissez-le comme attribut.
  • Dans la version 18, supposez que cela peut être l'un ou l'autre et suivez la stratégie la plus optimale pour cela.

Mais dans tous les cas, en gardant cette API séparée, les objets JSX et props sont conservés propres et dans un seul espace de noms, tout comme ils le sont pour les composants React et les éléments HTML non personnalisés.

Désolé pour les commentaires excessifs, mais j'ai pensé à un autre avantage de ma proposition ci-dessus que j'aimerais partager :

Ces appels ReactDOM.defineCustomElementProp() peuvent être fournis dans un fichier JS géré par l'auteur de l'élément personnalisé (dans le même référentiel que celui où l'élément personnalisé est géré/distribué). Cela ne serait pas nécessaire pour les éléments personnalisés avec une correspondance stricte 1: 1 de propriété / attribut, qui, selon la déclaration de fond de ce numéro, est de toute façon la recommandation et le cas majoritaire. Ainsi, seuls les auteurs d'éléments personnalisés ne suivant pas cette recommandation devront fournir le fichier d'intégration React. Si l'auteur ne le fournit pas (par exemple, parce que l'auteur de l'élément personnalisé ne se soucie pas de React), alors la communauté des personnes qui utilisent cet élément personnalisé dans les applications React pourrait auto-organiser un référentiel central pour héberger ce fichier d'intégration.

Je pense que la possibilité d'une telle centralisation est préférable à une solution qui oblige chaque utilisateur de l'élément personnalisé à toujours être explicite avec un sceau.

L'option 3 serait ma préférée mais c'est un énorme changement décisif... Et l'inverse ? Les attributs ont un préfixe et non des accessoires ?

Sigils dans React, je ne sais pas ce que je ressens à ce sujet. La spécification JSX doit être considérée comme universelle, pas trop dépendante ou dépendante des spécificités du navigateur, en particulier pas des irrégularités dues à la compatibilité descendante. obj[prop] = value et obj.setAttributes(props, value) se comportent différemment est malheureux, mais en regardant l'API du navigateur dans son ensemble, ce n'est pas une surprise. @ : [] ferait remonter à la surface un détail d'implémentation et contredirait l'approche centrée sur javascript. Donc, à moins que nous ayons une spécification qui fait ce qui suit, je pense que c'est une mauvaise idée : const data = <strong i="8">@myFunction</strong> // -> "[object Object]"

Si je dois m'appuyer sur un composant Web, je serais heureux que la sémantique soit cachée à React et JSX, tout en m'assurant qu'ils n'introduisent pas de changements de rupture. De toutes les options, laisser ref => ... en place me semble être favorable. ref est spécialement conçu pour accéder à l'objet. Et au moins le développeur sait exactement ce qui se passe, il n'y a pas de fuite de sceau, ni de nouveaux attributs qui pourraient casser des projets existants.

@LeeCheneler

L'option 3 serait ma préférée mais c'est un énorme changement décisif... Et l'inverse ? Les attributs ont un préfixe et non des accessoires ?

Pourquoi serait-ce un changement radical ? Le comportement actuel des attributs étant la valeur par défaut resterait. Le sigil serait opt-in et les développeurs l'utiliseraient pour remplacer les taches dans leur code où ils utilisent actuellement un ref pour transmettre des données à un élément personnalisé en tant que propriété JS.

@drcmda

ni de nouveaux attributs qui pourraient casser des projets existants.

Pouvez-vous préciser ce que vous entendez par là ?

Pour info pour tous ceux qui suivent la discussion, j'ai mis à jour la RFC avec une 5ème option suggérée par les membres de l'équipe React.

@robdodson

Je parlais de ça :

Option 4 : Ajouter un objet d'attributs
Les inconvénients
C'est peut-être un changement décisif

L'option 5 semble la plus sûre pour nous. Cela nous permet d'ajouter la fonctionnalité sans avoir à prendre de décision sur l'API « implicite » pour le moment puisque l'écosystème est toujours dans la phase de « le comprendre ». On pourra toujours le revoir dans quelques années.

L'élément d'entrée de papier de polymère a 37 propriétés, il produirait donc une très grande configuration. Si les développeurs utilisent beaucoup d'éléments personnalisés dans leur application, cela peut équivaloir à beaucoup de configurations qu'ils doivent écrire.

J'ai l'impression que les utilisateurs d'éléments personnalisés dans React voudront de toute façon encapsuler certains éléments personnalisés dans des composants React pour des comportements/personnalisations spécifiques à l'application. C'est une meilleure stratégie de migration pour ce cas si tout est déjà un composant React, par exemple

import XButton from './XButton';

et qui se trouve être généré par

export default ReactDOM.createCustomElementType(...)

Cela leur permet de remplacer un composant React par un composant personnalisé qui utilise (ou même n'utilise pas) des éléments personnalisés à tout moment.

Donc, si les gens vont créer des composants React aux points d'interopérabilité, nous pourrions aussi bien fournir une aide puissante pour le faire. Il est également probable que les gens partagent ces configurations pour les éléments personnalisés qu'ils utilisent.

Et finalement, si nous voyons l'écosystème se stabiliser, nous pouvons adopter une approche sans configuration.

Je pense que la prochaine étape ici serait d'écrire une proposition détaillée sur la façon dont la configuration devrait ressembler pour satisfaire tous les cas d'utilisation courants. Cela devrait être suffisamment convaincant pour les utilisateurs d'éléments personnalisés + React, car s'il ne répond pas aux cas d'utilisation courants (comme la gestion des événements), nous allons nous retrouver dans les limbes où la fonctionnalité ne fournit pas suffisamment d'avantages pour compenser la verbosité .

En partant de mon commentaire précédent , que diriez-vous de :

const XFoo = ReactDOM.createCustomElementType('x-foo', {
  propName1: {
    propertyName: string | null,
    attributeName: string | null,
    attributeSerializer: function | null,
    eventName: string | null,
  }
  propName2: {
  }
  ...
});

La logique serait alors, pour chaque prop React sur une instance XFoo :

  1. Si le eventName de cette prop n'est pas nul, enregistrez-le en tant que gestionnaire d'événements qui appelle la valeur de la prop (supposée être une fonction).
  2. Sinon, si le rendu côté client et que propertyName n'est pas nul, définissez la propriété de l'élément sur la valeur prop.
  3. Sinon, si attributeName n'est pas nul, définissez l'attribut d'élément sur la valeur prop stringified. Si attributeSerializer n'est pas nul, utilisez-le pour chaîner la valeur de la prop. Sinon, faites simplement '' + propValue .

L'élément d'entrée de papier de polymère a 37 propriétés, il produirait donc une très grande configuration.

Je voudrais suggérer que la configuration ne soit nécessaire que pour les accessoires aberrants. Pour tout accessoire sur l'instance XFoo qui n'était pas inclus dans la configuration, par défaut :

  • si la valeur est une fonction :
eventName: the prop name,
  • autre:
propertyName: the prop name,
attributeName: camelCaseToDashCase(the prop name),

Alternativement, il est peut-être logique de conserver les événements dans un espace de noms séparé, auquel cas, supprimez tout ce qui concerne eventName du dernier commentaire, et laissez plutôt les événements être enregistrés comme :

<XFoo prop1={propValue1} prop2={propValue2} events={event1: functionFoo, event2: functionBar}>
</XFoo>

@gaearon @effulgentsia que pensez-vous d'une combinaison de l'option 1 et de l'option 5 ?

L'option 1 permettrait à l'utilisateur occasionnel d'un élément personnalisé de transmettre plus facilement des données riches. J'imagine le scénario dans lequel je crée une application et je veux juste utiliser quelques éléments personnalisés. Je sais déjà comment ils fonctionnent et je ne suis pas tellement investi que je veux écrire une config pour eux.

L'option 5 serait destinée aux personnes qui souhaitent utiliser quelque chose comme la saisie papier dans toute leur application et aimeraient vraiment exposer l'intégralité de son API à tous les membres de leur équipe.

Pour le SSR de l'option 1, l'heuristique pourrait toujours utiliser un attribut lors du rendu sur le serveur. Une propriété camelCase est convertie en un attribut tiret-case. Cela semble être un modèle assez courant dans les bibliothèques de composants Web.

J'aime beaucoup l'idée d'une combinaison option1 + option5. Cela signifie que pour la plupart des éléments personnalisés :

<x-foo prop1={propValue1}>

fonctionnerait comme prévu : prop1 défini en tant que propriété côté client et en tant qu'attribut (en tirets) côté serveur.

Et les gens pourraient passer à l'option 5 pour tout ce qui ne leur convient pas.

Ce serait un changement radical par rapport à la façon dont React 16 fonctionne. Pour tous ceux qui subissent cette rupture (par exemple, ils utilisaient un élément personnalisé avec des attributs qui ne sont pas soutenus par des propriétés), ils pourraient passer à l'option5, mais c'est toujours une rupture. Je laisse à l'équipe React le soin de décider si c'est acceptable.

Ah, c'est ce que je reçois pour avoir lu ça rapidement dans le train @robdodson 🤦‍♂️ ... Pas vraiment fan de l'option 3 maintenant 🤔 Je l'ai lu comme un tout sur les accessoires étant préfixés, d'où mon hésitation.

L'option 5 semble raisonnable et simple.

J'aime où @effulgentsia se dirige. Y a-t-il une raison pour laquelle cela ne pourrait pas être :

const XFoo = ReactDOM.createCustomElementType('x-foo', {
  propName1: T.Attribute,
  propName2: T.Event,
  propName3: T.Prop
})

Ou est-il utile de prendre en charge plusieurs types sur un seul accessoire ?

J'hésiterais avec ce flux si

si la valeur est une fonction :
eventName : le nom de l'accessoire,
autre:
propertyName : le nom de l'accessoire,
attributeName : camelCaseToDashCase (le nom de l'accessoire),

Je ne pense pas que je voudrais qu'un accessoire de fonction soit par défaut un événement, et l'attribution à la fois de propertyName et de attributeName est-elle sensée? Quand voudriez-vous que les deux soient pris en charge pour imiter la question ci-dessus ? ??

@LeeCheneler :

Citant les avantages de l'option 1 du résumé du problème :

Tout élément créé avec des bibliothèques telles que Polymer ou Skate générera automatiquement des propriétés pour sauvegarder leurs attributs exposés. Ces éléments devraient tous « fonctionner » avec l'approche ci-dessus. Les développeurs qui créent à la main des composants vanille sont encouragés à sauvegarder les attributs avec des propriétés car cela reflète la façon dont les éléments HTML5 modernes (c'est-à-dire pas excentriques comme <input> ) ( <video> , <audio> , etc.) ont été mis en œuvre.

C'est donc la raison pour laquelle l'attribution à la fois de propertyName et de attributeName est sensée : car elle reflète ce qui est réellement le cas pour les éléments qui suivent les meilleures pratiques. Et en informant React de cela, cela permet à React de décider lequel utiliser en fonction de la situation : comme l'utilisation de propriétés pour le rendu côté client et d'attributs pour le rendu côté serveur. Pour les éléments personnalisés qui ne suivent pas les meilleures pratiques et ont certains attributs sans propriétés correspondantes et/ou certaines propriétés sans attributs correspondants, React devrait en être conscient, afin que les propriétés sans attribut ne soient pas rendues pendant SSR et la propriété -less-attributes peut être défini avec setAttribute() pendant le rendu côté client.

Avec votre proposition, cela pourrait potentiellement être fait en combinant des indicateurs de bits, tels que :

propName1: T.Property | T.Attribute,

Cependant, cela ne fournirait pas un moyen d'exprimer que le nom de l'attribut est différent du nom de la propriété (par exemple, camelCase à dash-case). Cela ne fournirait pas non plus un moyen d'exprimer comment sérialiser un objet riche en un attribut pendant la SSR (le comportement actuel de "[object Object]" n'est pas utile).

Je ne pense pas que je voudrais qu'un accessoire de fonction soit par défaut un événement

Oui, je pense que je suis d'accord avec cela aussi, d'où le commentaire de suivi . Merci d'avoir validé mon hésitation avec ça !

Voici une pensée pour une version moins détaillée de ma suggestion précédente :

const XFoo = ReactDOM.createCustomElementType('x-foo', {
  UNREFLECTED_ATTRIBUTES: [
    'my-attr-1',
    'my-attr-2',
  ],
  UNREFLECTED_PROPERTIES: [
    'myProp1',
    'myProp2',
  ],
  REFLECTED_PROPERTIES: {
    // This is default casing conversion, so could be omitted.
    someVeryLongName1: 'some-very-long-name-1',

    // In case anyone is still using all lowercase without dashes.
    someVeryLongName2: 'someverylongname2',

    // When needing to define a function for serializing a property to an attribute.
    someRichData: ['some-rich-data', JSON.stringify],
  },
});

Et selon le commentaire de code ci-dessus, je recommande vivement de ne pas exiger que chaque propriété reflétée soit définie, mais plutôt par défaut tout ce qui n'est pas défini comme étant automatiquement une propriété reflétée dont le nom d'attribut est la version en tiret.

C'est logique @effulgentsia 👍

J'aime votre deuxième exemple, mais n'est-il pas ouvert à l'explosion combinatoire si plus de types sont ajoutés, ala événements + tout ce qui pourrait avoir du sens?

- UNREFLECTED_ATTRIBUTES
- UNREFLECTED_PROPERTIES
- UNREFLECTED_EVENTS
- REFLECTED_PROPERTIES_ATTRIBUTES
- REFLECTED_PROPERTIES_EVENTS
- REFLECTED_ATTRIBUTES_EVENTS
- REFLECTED_PROPERTIES_ATTRIBUTES_EVENTS
...

Bien que je suppose que vous ne voudriez pas mélanger un événement avec un accessoire ou un attribut de toute façon 🤔 L'attribut et l'accessoire sont probablement les seules choses que vous voudriez imiter.

Je pense qu'il y a ici une opportunité pour la communauté React et Web Component de s'aligner sur une meilleure pratique. Réagir en ayant une opinion ici aidera grandement les auteurs d'éléments personnalisés à être guidés dans la bonne direction en raison de son adoption généralisée et du poids de ses opinions.

Bien que j'aie été l'auteur de l'implémentation de l'option 4, je suis toujours obligé de séparer les attributs et les événements des propriétés. Idéalement, je préférerais l'option 1. En pratique, je pense que je préférerais l'option 2 avec une trappe d'évacuation.

L'option 1 est idéale, mais il existe de nombreux types d'attributs qui n'ont pas de propriétés correspondantes (aria / données), cela nécessiterait donc des heuristiques supplémentaires autour de ceux-ci et s'il existe des cas extrêmes où les éléments peuvent avoir un attribut qui devrait avoir un propriété, mais n'en implémentez pas une pour quelque raison que ce soit. Je pense que c'est une option qui devrait être considérée avec soin, mais qui peut être viable à long terme.

L'option 2 est préférable car il s'agit d'un changement ininterrompu. Cela fonctionnera pour toutes les situations où la définition d'élément personnalisé est enregistrée avant la création d'une instance (ou chargée avant la définition des propriétés). L'art antérieur pour cette option est Preact (cc @developit) et cela a bien fonctionné jusqu'à présent. Suivre cette voie donne une implémentation raisonnablement robuste et incassable qui a été prouvée par une variante React réussie et qui fonctionne dans la plupart des situations. Au contraire, cela donne à React une solution à court terme tandis que de meilleures solutions sont évaluées à long terme.

Pour la situation (situation s ?) où cela ne fonctionne pas - chargement différé des définitions d'éléments personnalisés et de toutes les autres que nous n'avons pas couvertes - une trappe d'échappement comme ce que le DOM incrémental a fait , pourrait être implémentée. Ceci est similaire à ce que propose adapté au nombre de x d'éléments personnalisés. Si les consommateurs veulent le faire par élément personnalisé, ils le peuvent toujours, car il ne s'agit que d'une fonction. Cela permet à React d'avoir un avis, de s'échapper et de satisfaire tous les cas d'utilisation en transférant la responsabilité au consommateur pour tous les cas limites. C'est aussi quelque chose dont j'ai déjà discuté avec @developit à propos de l'implémentation dans Preact. L'alignement entre Preact / React ici serait une grande victoire.

À propos du problème avec les futurs attributs HTML, c'est quelque chose que nous ne pouvons pas résoudre, donc je ne pense pas que nous devrions nous en préoccuper ici.

Ces appels ReactDOM.defineCustomElementProp() pourraient être fournis dans un fichier JS maintenu par l'auteur de l'élément personnalisé

Cela lierait l'implémentation de l'élément personnalisé à l'implémentation spécifique à la bibliothèque (dans ce cas, React). À mon avis, c'est un fardeau trop lourd pour les auteurs d'éléments personnalisés. De plus, pour les auteurs qui n'utilisent pas React, les convaincre d'expédier la définition entre dans des discussions politiques dont personne ne veut.

Définir l'invocation d'une telle API par l'utilisateur d'un élément personnalisé avec uniquement les propriétés/attributs que l'utilisateur utilise réellement est moins de code à gérer et plus expressif.

Même si un auteur de bibliothèque n'utilise pas React, je pense que c'est une meilleure expérience utilisateur pour le client de ces composants Web de définir une fois la configuration de la propriété, puis de l'utiliser.

La première fois que vous utilisez un composant Web qui a besoin d'une propriété, c'est aussi difficile que d'ajouter un sigil (plus détaillé, mais aussi plus explicite) mais ensuite c'est plus facile pour les utilisations suivantes car vous n'avez pas besoin d'y penser pour chaque site d'appel de ce composant. Dans les deux cas, vous devez comprendre les différences de propriétés/attributs lors de l'adoption d'un nouveau composant Web, mais avec la configuration partagée unique, vous n'avez pas besoin que chaque utilisateur de ce composant au sein de la même application y réfléchisse.

Nous pourrions potentiellement autoriser le remappage des noms de propriété dans cette configuration pour permettre d'y créer davantage de noms idiomatiques React. Le chemin de mise à niveau vers un wrapper plus sophistiqué, le cas échéant, est également plus fluide puisque vous pouvez simplement remplacer XFoo par un composant React personnalisé qui effectue la logique de traduction sophistiquée dont il a besoin.

En gros : s'il est possible pour les gens de ne pas penser à la configuration de la propriété à chaque fois qu'ils utilisent un composant, je préfère cette approche. C'est pourquoi j'ai proposé l'option 5. Je pense qu'elle est plus ergonomique que les 3 et 4 tout en étant tout aussi flexible.

Les options 1 et 2 ne fonctionnent pas très bien avec le rendu du serveur (génération HTML), et l'option 2 crée également des risques de mise à niveau pour les auteurs de composants Web, où l'ajout d'une propriété portant le même nom qu'un attribut existant est un changement décisif. Si nous décidons que la SSR n'est pas utile, l'option 1 est plutôt attrayante du point de vue de React. Je ne sais pas si ce serait assez complet dans la pratique - les auteurs de composants exposent-ils tout via les propriétés ?

@treshugart sans entrer trop profondément dans le bikeshedding, pensez-vous que vous pourriez montrer comment vous vous attendriez à ce que la " trappe de sortie " fonctionne ? Le pseudo code c'est bien. Juste pour que nous soyons tous sur la même longueur d'onde.

Désolé @sophiebits vient de voir votre réponse. J'aimerais répondre à quelques-uns des points après l'avoir lu plusieurs fois, mais je voulais répondre rapidement à celui-ci :

Je ne sais pas si ce serait assez complet dans la pratique - les auteurs de composants exposent-ils tout via les propriétés ?

Tout composant construit avec Polymer ou Skate exposera tout via des propriétés. Il peut y avoir quelques rares valeurs aberrantes (peut-être définir un attribut juste pour le style ou quelque chose), mais sinon, je pense que les choses sont toujours soutenues par des propriétés. Je suis assez convaincu que la majorité des composants Web en production sont construits à l'aide de l'une de ces deux bibliothèques.

Si quelqu'un crée manuellement un composant Web vanille, rien ne l'oblige à tout exposer via des propriétés, mais nous l'encourageons à le faire dans nos documents et exemples de bonnes pratiques .

Pour ce que ça vaut, rien ne les oblige non plus à utiliser des attributs. Une définition d'élément personnalisé n'est qu'une classe afin que le développeur puisse faire ce qu'il veut. Mais en pratique, la plupart des gens préfèrent utiliser une abstraction comme Polymer ou Skate pour créer leurs composants, ils obtiennent donc une API de propriétés gratuitement.

Peut-être que la meilleure approche consiste alors à toujours créer des propriétés du côté client, puis à prendre en charge une carte de configuration de style Option 5 des noms de propriétés primitifs pour attribuer des noms aux personnes qui souhaitent prendre en charge la SSR dans leurs applications.

@sophiebits : Je pense que c'est une excellente idée, sauf dans la mesure où c'est un changement radical pour les personnes qui ont écrit leurs applications React en utilisant des noms d'attribut. Par exemple:

<x-foo long-name={val} />

Mais qu'en est-il d'une règle pour "do properties" si le propname n'a pas de tirets et "do attribute" s'il en a ?

@robdodson : connaissez-vous des frameworks ou des pratiques d'éléments personnalisés où les gens auraient des casses d'attributs différentes de la propriété correspondante, sans contenir de tiret ? C'est-à-dire un attribut longname avec une propriété longName ? Cela semble en fait être le modèle beaucoup plus courant avec des éléments HTML intégrés et des attributs globaux (par exemple, contenteditable => contentEditable ), mais je ne sais pas si c'est maintenant suffisamment découragé pour les attributs d'éléments personnalisés grâce à Polymer et Skate. Si le problème persiste, alors JSX existant avec :

<x-foo longname={val} />

échouerait en tant que jeu de propriétés si la propriété est longName .

@effulgentsia Je ne peux parler que pour Skate, mais nous ne recommandons d'utiliser l'API d'attribut que si vous écrivez du HTML, qui - à toutes fins utiles - peut également être classé comme rendu de serveur. Si vous faites quoi que ce soit via JS, vous devriez définir des accessoires. Notre API props automatiquement une synchronisation/désérialisation unidirectionnelle à partir des attributs, et dérive le nom de l'attribut en mettant en tiret le nom de la propriété (camelCase devient camel-case, etc.). Ce comportement dans son ensemble est configurable, mais nous encourageons la meilleure pratique à être celle mentionnée ci-dessus.

Comme beaucoup l'ont déclaré, les accessoires rendent la SSR difficile, et c'est une préoccupation valable. Puisqu'il semble que la plupart préféreraient l'option 1, peut-être essayons-nous d'aller de l'avant avec la proposition de @sophiebits de définir des accessoires sur le client tout en fournissant une solution de secours / mappage ? Je suppose que cela signifie que les attributs seront définis sur le serveur ?

À titre d'exemple, voici comment vous pouvez implémenter le transfert d'état et d'accessoires du serveur vers le client avec Skate (et tout élément personnalisé qui implémente un renderedCallback() et props et/ou state setters. Le faire au niveau de l'élément personnalisé est trivial. Si React décidait de définir des attributs sur le serveur, la réhydratation serait essentiellement la même. Les auteurs de composants Skate n'auraient en fait rien à faire comme nous leur fournirait déjà la logique de désérialisation.

@robdodson Je ferais une chose similaire à ce que fait le DOM incrémentiel. Cela ressemblerait à quelque chose comme :

const isBrowser = true; // this would actually do the detection
const oldAttributeHook = ReactDOM.setAttribute;

// This is much like the IDOM impl but with an arguably more clear name.
ReactDOM.setAttribute = (element, name, value) => {
  // This is essentially option 2, but with the added browser check
  // to keep attr sets on the server.
  if (isBrowser && name in element) {
    element[name] = value;
  } else {
    oldAttributeHook(element, name, value);
  }
};

@sophiebits

L'option 2 crée également des risques de mise à niveau pour les auteurs de composants Web, où l'ajout d'une propriété portant le même nom qu'un attribut existant est un changement décisif.

Je pense que cela peut être inévitable étant donné la façon dont les attributs fonctionnent dans la plate-forme. Si vous SSR un élément personnalisé et devez donc écrire dans un attribut, vous courez également le risque que la plate-forme expédie un attribut du même nom à l'avenir. Comme @treshugart l'a mentionné précédemment , je ne pense pas que ce soit quelque chose que React (ou vraiment n'importe quelle bibliothèque) soit habilité à résoudre. C'est quelque chose que je veux aborder avec les auteurs de spécifications de composants Web à TPAC pour voir si nous pouvons le corriger dans l'espace des éléments personnalisés.

Je ne sais pas si cela vous change du tout d'avis à propos de l'option 2 😁 mais je voulais le mentionner car l'option 2 a quelques bonus sympas et Preact semble le prouver dans la pratique.

Peut-être que la meilleure approche consiste alors à toujours créer des propriétés du côté client, puis à prendre en charge une carte de configuration de style Option 5 des noms de propriétés primitifs pour attribuer des noms aux personnes qui souhaitent prendre en charge la SSR dans leurs applications.

+1, je suis d'accord pour continuer à aller dans cette direction.


@effulgentsia

Je pense que c'est une excellente idée, sauf dans la mesure où c'est un changement radical pour les personnes qui ont écrit leurs applications React en utilisant des noms d'attribut.

L'option 2 résoudrait cela :)
Sinon, il faudrait probablement une heuristique couplée à l'option 1 qui mappe les attributs en tirets aux propriétés en camelCased.

Mais qu'en est-il d'une règle pour "do properties" si le propname n'a pas de tirets et "do attribute" s'il en a ?

Il semble que Preact définira l'attribut s'il y a un tiret dans le nom. Je suppose que c'est parce qu'ils utilisent l'option 2 et que long-name ne réussit pas le contrôle in donc il revient à un attribut ( source ).

Personnellement, j'aime ce comportement, mais il revient dans le domaine de la définition des attributs et des conflits futurs possibles, donc je pense que @sophiebits devrait peser.

êtes-vous au courant de cadres ou de pratiques d'éléments personnalisés où les gens auraient une casse d'attributs différente de la propriété correspondante, sans contenir de tiret ?

Pas que je sache de. Le tableau de bord est un moyen facile pour la bibliothèque de savoir où aller à dos de chameau. Si vous écriviez à la main un composant Web vanille, vous pourriez faire longname/longName et seule l'option 2 vous sauverait car elle ne trouverait pas longname propriété longname attribut.

Pour ce que ça vaut, il semble que Preact, en dernier recours, appellera toLowerCase() sur une propriété qu'il ne reconnaît pas avant de définir l'attribut. Donc, si vous faisiez <x-foo longName={bar}/> SSR longname="" .


@treshugart

peut-être essayons-nous d'aller de l'avant avec la proposition de @sophiebits de définir des accessoires sur le client tout en fournissant une solution de secours / mappage ? Je suppose que cela signifie que les attributs seront définis sur le serveur ?

Oui et oui.

Je ferais une chose similaire à ce que fait le DOM incrémental

Est-ce que l'idée que chaque élément (ou leur classe de base) devrait être mélangé ?

d'ailleurs, merci à tous de continuer à participer à la discussion, je sais que ça a été assez long ❤️

Je ne suis pas sûr de comprendre le dernier exemple dans https://github.com/facebook/react/issues/11347#issuecomment -339858314 mais il est très peu probable que nous fournissions un hook global remplaçable comme celui-ci.

@gaearon Je pense que Trey montrait simplement le changement net comme un patch singe, vraisemblablement il serait écrit dans l'implémentation ReactDOM elle-même.

Sur la base des commentaires récents, que pensez-vous tous de cette proposition ?

  1. Pour BC, ne changez rien sur le fonctionnement des éléments personnalisés en minuscules dans React. En d'autres termes, JSX comme <x-foo ... /> continuerait à fonctionner de la même manière dans React 17 que dans 16. Ou, si des corrections de bugs _mineurs_ sont souhaitées, ouvrez un numéro séparé pour en discuter.

  2. Ajoutez une API sans configuration pour créer un composant React avec une meilleure sémantique des éléments personnalisés. Par exemple, const XFoo = ReactDOM.customElement('x-foo'); .

  3. Pour le composant créé ci-dessus, utilisez la sémantique suivante :

  4. Pour tout accessoire sur XFoo qui est un nom d'accessoire React réservé ( children , ref , d'autres ?), appliquez-lui la sémantique habituelle de React (ne le définissez pas comme un attribut ou une propriété sur le nœud DOM de l'élément personnalisé).
  5. Pour tout attribut ou propriété global HTMLElement défini par la spécification HTML (y compris data-* et aria-* ), faites la même chose avec ces accessoires que ce que React ferait avec eux s'ils étaient sur un div Élément <XFoo data-x={} className={} contentEditable={} /> au nœud DOM doit être identique à la façon dont il le fait pour <div data-x={} className={} contentEditable={} /> (à la fois pour le côté client et pour le SSR).
  6. Pour tout accessoire sur XFoo qui contient un tiret (autre que les attributs globaux autorisés ci-dessus), émettez un avertissement (pour informer le développeur que XFoo est une API centrée sur les propriétés et non sur les attributs) et ne faites rien avec (ni le définir comme une propriété ni un attribut).
  7. Pour tout accessoire sur XFoo qui commence par un trait de soulignement ou une lettre ASCII supérieure, ne faites rien avec (et peut-être émettre un avertissement ?). Ni l'un ni l'autre n'est une méthode recommandée pour nommer les propriétés des éléments personnalisés, alors réservez ces espaces de noms pour la sémantique future (par exemple, voir #4 ci-dessous).
  8. Pour tous les autres accessoires sur XFoo, lors du rendu côté client, définissez-le sur le nœud DOM en tant que propriété. Pour SSR, si la valeur est primitive, rendez-la sous forme d'attribut ; s'il n'est pas primitif, ignorez le rendu et définissez-le comme propriété côté client pendant l'hydratation. Pour le rendu SSR en tant qu'attribut, camelCaseToDashCase le nom.

  9. Pour les composants créés via l'API dans #2 ci-dessus, réservez un nom de prop à utiliser pour les écouteurs d'événement. Par exemple, 'events' ou 'eventListeners' . Ou, si vous ne voulez pas entrer en conflit avec ces noms de propriétés d'éléments personnalisés potentiels, alors '_eventListeners' ou 'EventListeners' . L'implémentation créée par ReactDom de XFoo enregistrerait automatiquement ces écouteurs d'événement sur le nœud DOM.

  10. Pour les cas extrêmes (par exemple, en utilisant un élément personnalisé pour lequel l'implémentation ci-dessus n'est pas souhaitée ou suffisante), le développeur de l'application peut implémenter son propre composant React pour faire tout ce dont il a besoin. C'est-à-dire qu'ils n'ont pas besoin d'utiliser ReactDOM.customElement() ou qu'ils pourraient l'étendre.

  11. Pour les personnes qui souhaitent tout le comportement ci-dessus, mais qui souhaitent que leur JSX utilise les noms d'éléments personnalisés en minuscules ( <x-foo /> au lieu de <XFoo /> , pour des raisons similaires que les personnes familiarisées avec l'écriture HTML préfèrent <div> sur <Div> ), ils peuvent appliquer un patch singe à React.createElement(). Ce serait un patch singe assez simple: prenez simplement le premier argument et s'il correspond à un nom d'élément personnalisé pour lequel vous le souhaitez (qu'il s'agisse d'une liste spécifique ou d'une chaîne avec toutes les lettres minuscules et au moins un tiret), puis invoquez ReactDOM.customElement() sur ce nom et transmettez le résultat et les arguments restants au React.createElement() .

@developit @gaearon cela pourrait être l'un ou l'autre. Si une "cartographie" était nécessaire, je pense qu'un crochet est plus durable. Cependant, cela visait également à montrer le changement net s'il devait être implémenté dans le noyau de ReactDOM, comme l'a souligné Jason.

Pour BC, ne changez rien sur le fonctionnement des éléments personnalisés en minuscules dans React. En d'autres termes, JSX aime continuerait à fonctionner de la même manière dans React 17 que dans 16. Ou, si des corrections de bogues mineures sont souhaitées, ouvrez un numéro séparé pour en discuter.

Personnellement, je préférerais pouvoir utiliser mon élément <x-foo> , tel quel, et lui transmettre facilement des propriétés sans avoir besoin de l'envelopper au préalable.

Si possible, je préfère utiliser la suggestion de @sohpiebits ' des options 1 (propriétés côté client) et 5 (config pour SSR). J'espère toujours que les gens reconsidéreront peut-être l'option 2 (peut-être l'option 2 + 5 ?) pour le bonus de compatibilité descendante.

@gaearon, votre opinion change-t-elle sur la proposition de Trey si elle fait partie du noyau ReactDOM ? Peut-être pourrions-nous étoffer davantage l'exemple si cela pouvait aider?

Ma principale préoccupation avec l'option 5 est de créer beaucoup de passe-partout pour permettre l'interopérabilité, en cassant le DX, il serait dommage que toute l'équipe principale de React et les contributeurs passent beaucoup de temps dans des changements qui n'auront pas l'impact souhaité.

J'ai vraiment adoré la façon dont l'équipe React a géré les derniers changements dans le repo, comme _PropTypes_, il serait bon de penser à un plan impliquant plus d'un seul commutateur, pour informer les développeurs sur les changements possibles à faire à l'avenir pour la personnalisation et non la personnalisation éléments.

Je pense que la solution qui nous satisfera tous sera une combinaison de certaines de ces options comme _étapes_, ajout d'API, avertissement et dépréciation ou changement de comportement.

Peut-être les options 5, avec avertissement, plus tard l'option 2 avec la dépréciation du wrapper nécessaire.

Peut-être les options 5, avec avertissement, plus tard l'option 2 avec la dépréciation du wrapper nécessaire.

Je pensais en fait que faire le contraire serait une meilleure série d'étapes. Option 1 ou 2 parce que c'est un changement moins spectaculaire. Et mesurer comment cela se passe et à quoi l'histoire de la SSR commence à ressembler pour les composants Web. Puis suivi de l'option 5 car elle ajoute une nouvelle API et une notion de composants proxy/wrapper.

Le principal problème avec l'option 1 est qu'il s'agit d'une pause BC assez importante. Le principal problème avec l'option 2 est qu'elle a une condition de concurrence selon le moment où l'élément est mis à niveau. Je ne suis pas sûr que l'un ou l'autre soit vraiment acceptable pour un projet aussi largement utilisé que React.

Compte tenu de la disponibilité de l'option 5, je me demande si nous pouvons à la place apporter des améliorations beaucoup plus sûres, mais toujours utiles, aux utilisations autres que l'option 5. Par exemple, que diriez-vous de l'inverse de l'option 4 : introduisez simplement les accessoires domProperties et eventListeners , afin que vous puissiez faire des choses comme :

<x-foo 
  my-attr1={...} 
  domProperties={{myRichDataProperty: ...}} 
  eventListeners={{'a-custom-element-event':  e => console.log('yo')}} 
/>

Ceci est entièrement rétrocompatible, car en raison de leurs lettres majuscules, domProperties et eventListeners ne sont pas des noms d'attribut valides . À part cela, React 16 appelle actuellement setAttribute() même pour les noms d'accessoires avec des alphas majuscules, en s'appuyant sur les navigateurs pour le nom en minuscules en interne ; Cependant, une future version mineure de React 16 pourrait-elle émettre un avertissement lorsqu'elle rencontre des accessoires d'élément personnalisés qui ne sont pas des noms d'attribut valides, afin que les utilisateurs puissent corriger leurs erreurs de casse avant de passer à React 17 ?

En plus de préserver BC, cette approche est facile à comprendre : elle est centrée sur les attributs (ce qui signifie que les accessoires React sont traités comme des attributs d'élément), ce qui convient étant donné que le nom de l'élément lui-même est entièrement en minuscules et comporte un tiret et est donc centré sur HTML. Cependant, domProperties est fourni pour les cas relativement rares où vous devez transmettre des propriétés, par exemple pour transmettre des données riches.

Pour les personnes qui souhaitent que leur modèle mental soit centré sur la propriété (ala Option 1), c'est là qu'une API de style option 5 pourrait intervenir :

const XFoo = ReactDOM.customElement('x-foo');
<XFoo prop1={} prop2={} data-foo={} aria-label={} />

Avec cette syntaxe, chaque prop est traité comme une propriété (option 1). Ce qui convient, car l'élément "nom" (XFoo) suit également une convention centrée sur JS. Je pense que nous voudrions toujours au minimum prendre en charge les accessoires data-* et aria-* traités comme des attributs, que nous pourrions soit limiter à ceux-ci, soit généraliser en traitant n'importe quel accessoire avec un tiret comme un attribut.

Pendant ce temps, pour prendre en charge la configuration de l'option 5 de SSR, nous pourrions ajouter une API de configuration à ReactDOM.customElement , telle que :

const XFoo = ReactDOM.customElement('x-foo', ssrConfiguration);

Peut-être que ssrConfiguration pourrait être une fonction de rappel similaire à celle de ReactDOM.setAttribute dans le commentaire de @treshugart ?

Qu'est-ce que tu penses?

@effulgentsia J'aime où vont vos pensées. Bikeshed un peu les noms : domProps / domEvents . C'est très proche de l'option 4.

WRT SSR, je pense que SSR peut être géré par les éléments personnalisés eux-mêmes tant que React pourrait respecter les attributs que le composant mute sur lui-même s'il ne les suit pas. J'ai posté un résumé plus tôt, mais le voici à nouveau, pour plus de commodité : https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6. Essentiellement, le composant applique les attributs après le rendu sur le serveur et les réhydrate sur le client. La SSR pour les composants Web est possible, mais des solutions sont toujours en cours de discussion au niveau des normes, il est donc préférable d'attendre cette partie de la proposition.

@effulgentsia J'aime aussi où tu te @sophiebits , @gaearon do
vous avez tous des idées sur cette direction?

Le mardi 31 octobre 2017, 19 h 33, Trey Shugart [email protected] a écrit :

@effulgentsia https://github.com/effulgentsia J'aime où votre
les pensées vont. Bikeshed un peu les noms, il pourrait être utile de
alignez leur nom : domProps / domEvents, ou quelque chose.

WRT option 2, il est au moins rétrocompatible et résout la plupart des cas d'utilisation,
mais j'en viens aux alternatives.

Je pense que SSR peut être géré par les éléments personnalisés eux-mêmes tant que
React pourrait respecter les attributs que le composant mute sur lui-même s'il est
ne pas les suivre. J'ai posté un résumé plus tôt, mais le revoilà, pour
commodité:
https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6.
Essentiellement, le composant applique les attributs après le rendu sur le serveur
et les réhydrate sur le client. SSR pour les composants Web est possible, mais
des solutions sont encore en cours de discussion au niveau des normes, il peut donc être
mieux vaut attendre cette partie de la proposition ici.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/react/issues/11347#issuecomment-340960798 ,
ou couper le fil
https://github.com/notifications/unsubscribe-auth/ABBFDeiQhBWNGXNplbVV1zluYxT-ntFvks5sx9hngaJpZM4QD3Zn
.

Bikeshedding un peu les noms : domProps / domEvents.

J'aime ceux-là. Je réfléchis également à la possibilité de les rendre encore plus idiomatiques à React en remplaçant le concept de "dom" par le concept de "ref". Donc, en d'autres termes : refProps / refEvents , puisqu'il s'agit d'attacher des accessoires et des écouteurs d'événement au "ref".

Et puis j'ai pensé, et si au lieu d'introduire de nouveaux noms spéciaux à l'intérieur de this.props , nous surchargeions simplement l'attribut JSX ref existant. Actuellement, "ref" est une fonction appelée lorsque le composant React est monté et démonté. Et si nous permettions qu'il soit aussi un objet comme suit :

<x-foo my-attr-1={}
  ref={{
    props: ...
    events: ...
    mounted: ...
    unmounted: ...
  }}
/>

Juste une idée.

J'aime beaucoup cette approche :)

Ping amical à ce sujet. @gaearon @sophiebits avez -vous tous des idées sur la dernière proposition de @effulgentsia ? Juste curieux de savoir si c'est dans le stade ou un non-partant.

Nous venons d'ouvrir un processus RFC. Pourrions-nous demander à l'un de vous de le soumettre en tant que RFC ?
https://github.com/reactjs/rfcs

Je préfère ne pas ajouter domProperties et eventListeners "props" (ou l'équivalent dans un objet ref={{}}) car cela rend l'utilisation d'éléments personnalisés très peu naturelle et contrairement à tous les autres composants React. Si l'utilisateur du composant doit connaître précisément la différence entre les propriétés et les attributs, etc., je préfère le faire en tant que solution de style ReactDOM.createCustomElementType. Ensuite, si vous utilisez le composant exactement une fois, cela représente une quantité de travail comparable (spécifier la configuration puis l'utiliser une fois), mais si vous utilisez le composant plusieurs fois, vous n'avez pas besoin de penser à l'objet de configuration à chaque fois. Exiger que la configuration soit spécifiée à chaque fois semble aller à l'encontre des objectifs d'une intégration d'éléments personnalisés propre, à moins que quelque chose ne me manque.

@sophiebits OK, je pourrais essayer d'écrire un RFC pour quelque chose comme ça. Que pensez-vous de l'idée que vous avez émise le 26 octobre d'aborder les propriétés côté client et de permettre également aux gens d'écrire ReactDOM.createCustomElementType pour SSR et/ou s'ils veulent un contrôle très fin sur la façon dont le client mappe les attributs/propriétés ?

Au moins avec le style createCustomElementType , les bibliothèques peuvent facilement y mapper leurs API. C'est-à-dire que notre skatejs/renderer-react pourrait facilement prendre le props et configurer l'élément personnalisé pour React. Ce genre de feuilles laisse les gens à la vanille haut et secs sans abstraction ou sans faire un peu de travail, cependant. J'aime la suggestion de Rob d'une valeur par défaut sûre, tout en permettant un contrôle fin. Est-ce quelque chose qui fonctionnerait?

@robdodson Je pense que je suis d'

L'option 3 est la meilleure option jusqu'à présent, et elle peut fonctionner avec le SSR, je vais vous expliquer comment.

Jusqu'à présent, toutes les options par elles-mêmes, à l'exception de 3,

  • soit créer une charge de maintenance élevée pour tout le monde en dehors de la source React
  • ou ils forcent tous les auteurs d'éléments personnalisés dans l'univers entier à respecter les règles spécifiques à React.

Voici des règles universelles sur lesquelles nous sommes tous d'accord :

  • définir des attributs avec setAttribute est le moyen le plus standard dans l'univers pour transmettre des données aux éléments d'une manière qui correspond 1 à 1 avec les attributs HTML déclaratifs. Cela doit fonctionner 100% du temps comme une loi de l'univers, c'est donc le seul moyen 100% garanti de transmettre des données aux éléments.
  • certaines personnes ne sont pas contentes qu'il ait été conçu uniquement pour les cordes.
  • Certains éléments (je le répète, _seulement certains éléments_ ), acceptent des valeurs via des propriétés d'objet qui correspondent à certains attributs. Ce n'est _pas_ quelque chose sur lequel on peut se fier à 100 % du temps.
  • certaines personnes aiment les propriétés d'objet car elles peuvent accepter des non-chaînes

Donc, au strict minimum, si React veut travailler à 100% avec _chaque élément personnalisé de l'univers et ne pas être un fardeau pour les gens_ , alors :

  • React doit se rendre compte que ce n'est pas Dieu, il y a beaucoup d'autres bibliothèques que les gens utilisent en plus de réagir,
  • Par conséquent, React devrait _par défaut_ simplement transmettre les données via setAttribute car c'est 100% standard.
  • React doit accepter le fait que les auteurs d'éléments personnalisés peuvent étendre/redéfinir les méthodes setAttribute dans leurs définitions de classe, ce qui fait que setAttribute accepte des choses autres que des chaînes. Un excellent exemple peut être des bibliothèques comme A-frame.
  • React devrait accepter que si un auteur d'élément personnalisé veut qu'un élément personnalisé fonctionne avec toutes les bibliothèques possibles, _pas seulement avec React_ alors cet auteur s'appuiera sur setAttribute pour rendre son élément par défaut compatible avec tout, et si par défaut toutes les bibliothèques reposent sur des attributs, alors tout l'univers fonctionnera les uns avec les autres. _Il n'y a pas de si, ni de mais à ce sujet !_ (à moins que le w3c/whatwg n'apporte de gros changements)

Sooooooooo , cela étant dit, nous devrions au moins

Ensuite, nous pouvons réfléchir aux implications des éléments ayant parfois une API de propriété d'objet :

  • les développeurs ont le choix d'utiliser setAttribute sachant que cela fonctionnera tout le temps.
  • les développeurs peuvent parfois tirer parti des propriétés des objets.
  • les développeurs doivent toujours savoir s'il existe ou non une interface objet-propriété pour un élément donné (personnalisé ou non).
  • Les interfaces de propriété d'objet sont une interface alternative qui ne correspond pas nécessairement 1 à 1 avec des attributs.

Ainsi, avec cette connaissance des attributs par rapport aux propriétés, une solution dans React qui _souhaite augmenter la norme à 100% et respecter les lois de l'univers_ devrait :

  1. autoriser les attributs à fonctionner 100 % du temps par défaut. Cela signifie que <x-foo blah="blah" /> doit _par défaut_ correspondre à setAttribute et transmettre la valeur avec _unchanged_ . Il s'agit d'un changement ininterrompu. En fait, il s'agit d'un changement de correction qui entraînerait autrement la transmission d'une chaîne "[object Object]" sans signification.
  2. Proposez un autre moyen de laisser l'utilisateur de React

Il semble que l'option 3, utilisant un sceau (dont la syntaxe supplémentaire n'est honnêtement pas difficile à apprendre), est une solution qui se rapproche le plus de l'idéal. Sur la base de cette question SO , le seul symbole disponible est = , bien que s'installer sur quelque chose comme & (avec une forme échappable, peut-être comme \& ) soit plus lisible. Par exemple, si je veux un accessoire spécifiquement :

<x-foo &blah="blah" />

La plupart des autres caractères couverts par la spécification de syntaxe HTML WHATWG devraient fonctionner, et j'espère qu'ils le feront, mais c'est un autre sujet.

L'option 3 est la meilleure option jusqu'à présent.

Si l'hydratation n'est pas utilisée sur le client et que, par conséquent, les accessoires n'ont pas de sens en SSR, alors, eh bien. Cela n'a jamais fonctionné auparavant, et cela n'a pas besoin de fonctionner maintenant. Le SSR de style PHP ou Java envoie du HTML statique sans hydratation et repose à 100 % sur les attributs. Cela veut dire que si nous utilisons React SSR, nous utiliserons probablement l'hydratation côté client, et si nous ne voulons pas d'hydratation, nous devons simplement être conscients du fait que nous ne devrions pas utiliser d'accessoires dans ce cas. _C'est simple. Tout ce que réagir a à faire est de clarifier cette mise en garde dans la documentation._

Mais!!! Ce n'est pas tout! Nous pouvons toujours inclure les fonctionnalités de l'option 5 pour donner aux gens plus de contrôle. Avec une API comme Option 5,

  • Certaines personnes peuvent configurer la façon dont certains attributs peuvent être mappés aux accessoires
  • ET comment certains accessoires peuvent correspondre aux attributs. Cela peut aider les gens à faire fonctionner le SSR même pour le type de SSR non hydratant !
  • nous pouvons laisser les gens définir "si cet attribut est écrit, le transmettre réellement à cet accessoire, mais s'il s'agit de SSR, ne le passez pas à l'accessoire", ou "passez cet attribut à cet attribut uniquement pendant le SSR, sinon faites quoi J'ai spécifié en utilisant le sceau", etc.

En fin de compte, ce qui suit semble être une solution qui fonctionnerait:

  • Option 3 avec setAttribute utilisé par défaut en coulisses,
  • avec un correctif pour #10070 afin que React ne gêne pas les gens,
  • et une API comme dans l'option 5 pour un réglage plus poussé pour ceux qui en ont vraiment besoin, y compris des moyens de régler le SSR, le client ou les deux.

Bonne année!

Je veux répondre à certains des points ci-dessus, mais je crains que ce fil ne soit déjà _incroyablement_ long. Alors désolé d'avoir fait plus long :P

Voici des règles universelles sur lesquelles nous sommes tous d'accord :

La définition d'attributs avec setAttribute est le moyen le plus standard dans l'univers pour transmettre des données aux éléments d'une manière qui correspond 1 à 1 avec les attributs HTML déclaratifs. Cela doit fonctionner 100% du temps comme une loi de l'univers, c'est donc le seul moyen 100% garanti de transmettre des données aux éléments.

Ce n'est pas tout à fait vrai. Il n'y a rien dans la plate-forme pour imposer qu'un élément personnalisé expose une interface d'attributs. Vous pouvez tout aussi bien en créer un qui n'accepte que les propriétés JS. Ce n'est donc pas un "moyen garanti de transmettre des données". L'absence de mécanismes d'application signifie que vous ne pouvez pas vous fier à l'un ou l'autre style (attributs HTML ou propriétés JS) avec une certitude à 100 %.

certaines personnes ne sont pas contentes qu'il ait été conçu uniquement pour les cordes.
Certains éléments (je le répète, seulement certains éléments), acceptent des valeurs via des propriétés d'objet qui correspondent à certains attributs. Ce n'est pas quelque chose sur lequel on peut se fier à 100% du temps.

Nous encourageons les gens à ne même pas se donner la peine de créer des attributs pour les propriétés qui acceptent des données riches comme des objets/tableaux. Cela est dû au fait que certaines données ne peuvent pas être sérialisées en une chaîne d'attributs. Par exemple, si l'une de vos propriétés d'objet est une référence à un nœud DOM, cela ne peut pas réellement être stringifié. De plus, lorsque vous chaînez et analysez un objet, il perd son identité. Cela signifie que s'il a une référence à un autre POJO, vous ne pouvez pas réellement utiliser cette référence puisque vous avez créé un tout nouvel objet.

certaines personnes aiment les propriétés d'objet car elles peuvent accepter des non-chaînes

Par conséquent, React devrait par défaut simplement transmettre les données via setAttribute car c'est 100% standard.

Les propriétés JavaScript sont également standard. La plupart des éléments HTML exposent à la fois une interface d'attributs et de propriétés correspondantes. Par exemple, <img src=""> ou HTMLImageElement.src .

React doit accepter le fait que les auteurs d'éléments personnalisés peuvent étendre/redéfinir les méthodes setAttribute dans leurs définitions de classe, faisant en sorte que setAttribute accepte des choses autres que des chaînes.

Les auteurs pourraient le faire, mais cela semble en fait beaucoup plus "non standard" que d'utiliser simplement les propriétés JS. Cela peut également vous exposer à des problèmes étranges liés à l'analyse et au clonage de l'élément .

React devrait accepter que si un auteur d'élément personnalisé veut qu'un élément personnalisé fonctionne avec toutes les bibliothèques possibles, pas seulement avec React, cet auteur s'appuiera sur setAttribute pour rendre son élément par défaut compatible avec tout, et si par défaut toutes les bibliothèques s'appuient sur des attributs , alors l'univers entier fonctionnera les uns avec les autres. Il n'y a pas de si, ni de mais à ce sujet ! (à moins que le w3c/whatwg fasse de gros changements)

Je ne sais pas comment vous arrivez à cette conclusion car il existe d'autres bibliothèques qui préfèrent définir des propriétés JS sur des éléments personnalisés (Angular, par exemple). Pour une compatibilité maximale, les auteurs doivent sauvegarder leurs attributs avec des propriétés JS. Cela couvrira la plus grande surface d'utilisations possibles. Tous les éléments créés avec Polymer le font par défaut.

autoriser les attributs à fonctionner 100 % du temps par défaut. Cela signifie que devrait par défaut correspondre à setAttribute et transmettre la valeur inchangée. Il s'agit d'un changement ininterrompu. En fait, il s'agit d'un changement de correction qui entraînerait autrement la transmission d'une chaîne "[object Object]" sans signification.

Je pense que React _is_ en passant la valeur inchangée. L'appel de setAttribute('foo', {some: object}) donne [object Object] . À moins que vous ne proposiez qu'ils appellent JSON.stringify() sur l'objet ? Mais alors cet objet n'est pas "inchangé". Je pense que vous comptez peut-être sur l'auteur pour remplacer setAttribute() ? Il peut être plus plausible de les encourager à créer des propriétés JS correspondantes au lieu de patcher le DOM.

Je pense que React _is_ en passant la valeur inchangée. Appeler setAttribute('foo', {some: object}) donne [object Object]

React force des valeurs à une chaîne avant de passer à setAttribute :

https://github.com/facebook/react/blob/4d6540893809cbecb5d7490a77ec7ad32e2aeeb3/packages/react-dom/src/client/DOMPropertyOperations.js#L136

et

https://github.com/facebook/react/blob/4d6540893809cbecb5d7490a77ec7ad32e2aeeb3/packages/react-dom/src/client/DOMPropertyOperations.js#L166

Je suis fondamentalement d'accord avec tout ce que vous avez dit.

Nous convenons que les gens font les choses dans les deux sens, et il n'y a pas de norme qui oblige tout le monde à le faire d'une manière ou d'une autre, donc je pense toujours

  • Option 3 avec setAttribute utilisé par défaut et un sceau pour spécifier d'utiliser les propriétés d'instance,
  • avec un correctif pour #10070 afin que React ne force pas les arguments en chaînes,
  • et Option 5, une API pour le réglage fin

Si l'option 5 est bien exécutée, l'hydratation pour les solutions SSR peut mapper les données sur des attributs ou des accessoires, comme cela peut être spécifié par l'utilisation de l'API de l'option 5.

React force les valeurs à une chaîne avant de passer à setAttribute

Je vois. Comme la plupart des gens ne définissent pas de toString() par défaut est [object Object] .

Comme la plupart des gens ne définissent pas de toString() par défaut est [object Object] .

Juste comme si je le faisais

const div = document.createElement('div')
div.setAttribute('foo', {a:1, b:2, c:3})

le résultat est

<div foo="[object Object]"></div>

Évidemment, en tant que développeurs Web, nous devons être conscients de ce qui se passe lorsque nous passons une non-chaîne dans les attributs d'un élément. Par exemple, je suis conscient que je peux passer des non-chaînes dans des éléments A-Frame, et je devrais être libre de le faire sans qu'une bibliothèque ne me gêne.

React doit se rendre compte que ce n'est pas Dieu, il y a beaucoup d'autres bibliothèques que les gens utilisent en plus de réagir

C'est inutilement sarcastique. Vous remarquerez dans ce fil que nous nous soucions de cela, mais qu'il existe de nombreuses options et visions différentes pour savoir où prendre la conception d'éléments personnalisés. Ce qu'il faut faire n'est certainement pas évident.

@sebmarkbage Désolé, je ne voulais pas du tout être sarcastique, et je pense que React est une belle bibliothèque. J'aurais dû réfléchir plus attentivement à mes paroles là-bas (surtout parce que tout le monde n'a pas la même religion).

Ce que je voulais dire, c'est que React est très populaire, donc React a le potentiel d'influencer les gens pour qu'ils fassent des choses d'une certaine manière qui peuvent ne pas fonctionner dans d'autres endroits (par exemple, cela pourrait dire aux gens de s'appuyer sur les propriétés d'instance pour tous les éléments personnalisés qui ne fonctionne avec tous les éléments personnalisés).

React convertit actuellement toutes les valeurs transmises aux attributs d'élément en chaînes. Si React ne l'avait pas fait, par exemple, il n'y aurait même pas besoin d' aframe-react (qui contourne le problème de chaîne) pour exister.

Si React peut simplement nous laisser la possibilité de faire n'importe quel choix sur la façon dont nous transmettons les données aux éléments, tout comme en JavaScript simple, cela ferait de moi l'utilisateur le plus satisfait. ??

Encore une fois, désolé pour mon choix de mots. J'y penserai à deux fois la prochaine fois.

J'ai ajouté un commentaire au RFC PR pour cela. Je pense que cela vaut la peine d'en discuter car il couvre ce qui est proposé ainsi qu'un modèle plus simple pour déduire de manière fiable un élément personnalisé et ses propriétés. Il le transforme à nouveau en une approche hybride, mais offre un moyen d'intégration sans configuration pour la plupart des cas d'utilisation.

Je suis impatient de voir cette fonctionnalité implémentée dans React. Merci beaucoup pour vos efforts pour rendre React génial.

Une mise à jour sur cette RFC?

Les bibliothèques d'éléments personnalisés deviennent vraiment bonnes et j'aimerais les utiliser dans mon application React. Des nouvelles à ce sujet ? L'encapsulation d'éléments personnalisés et la chaîne de leur contenu pour les analyser à nouveau ultérieurement est une solution assez impraticable étant donné que Vue et Angular gèrent les composants nativement avec facilité

Une mise à jour sur ce problème?

Moi aussi, j'aimerais utiliser des bibliothèques d'éléments personnalisés sans recourir à des hacks. J'aimerais que ce problème soit résolu.

L' équipe

Salut tout le monde, j'ai lancé une pull request pour résoudre ce problème en modifiant deux lignes : https://github.com/facebook/react/pull/16899

Cela permet à un auteur d'élément personnalisé de faire quelque chose comme ce qui suit :

class MyElement extends HTMLElement {
  setAttribute(name, value) {
    // default to existing behavior with strings
    if (typeof value === 'string')
      return super.setAttribute(name, value)

    // but now a custom element author can decide what to do with non-string values.
    if (value instanceof SomeCoolObject) { /*...*/ }
  }
}

Il existe de nombreuses variantes de ce à quoi pourrait ressembler une méthode setAttribute étendue, ce n'est qu'un petit exemple.

React team, vous pouvez affirmer que les auteurs d'éléments personnalisés ne devraient pas le faire, car ils contournent la gestion des attributs natifs du DOM dans certains cas (par exemple lorsque les valeurs ne sont pas des chaînes). Si vous avez cette opinion, cela ne signifie toujours pas que vous devez entraver ce que les auteurs d'éléments personnalisés peuvent faire avec leurs __ éléments personnalisés __.

React ne devrait pas se prononcer sur la façon dont les API DOM existantes sont utilisées. React est un outil pour manipuler le DOM, et ne doit pas se prononcer sur ce que nous pouvons faire au DOM, mais uniquement sur la manière dont les données circulent vers le DOM. Par "chemin des données vers le DOM", j'entends le chemin emprunté par les données pour y arriver, sans mutation des données (convertir les objets d'un auteur en chaînes, c'est muter les données de l'auteur).

Pourquoi voulez-vous muter les données de l'auteur ? Pourquoi ne pouvez-vous pas simplement supposer que la personne qui souhaite manipuler le DOM sait quelles données transmettre au DOM ?

@trusktr Je pense que cela a été discuté dans https://github.com/facebook/react/issues/10070

Je déconseille vraiment aux auteurs d'éléments personnalisés de remplacer une méthode intégrée telle que setAttribute . Je ne pense pas qu'il soit destiné à nous de singe le patch. cc @gaearon

Il est inoffensif de surcharger setAttribute dans des sous-classes comme ça (ce n'est pas un patch de singe). Mais comme je l'ai mentionné, pourquoi React doit-il dicter cela, si ce n'est pas nécessairement le travail de la bibliothèque React. Nous voulons utiliser React pour manipuler le DOM (sans entrave).

Si je dois utiliser el.setAttribute() manuellement pour améliorer les performances, cela ne fait qu'aggraver l'expérience de développement.

Je ne pense pas que l'équipe React sauve beaucoup de gens d'un énorme péril en convertissant tout ce qui est passé en setAttribute en chaînes.

Je suis d'accord qu'une autre solution pourrait être meilleure. Par exemple, mettre à jour la spécification JSX pour avoir une nouvelle syntaxe, mais cela semble prendre du temps.

Que perd la communauté si nous supprimons la conversion automatique des chaînes ? Que perd l'équipe React ?

L'équipe React pourrait améliorer la situation plus tard, avec une meilleure solution...

Pourquoi ne pas au moins nous donner simplement une option pour contourner la stringification ? Est-ce quelque chose que vous pourriez être prêt à considérer?

Je déconseille vraiment aux auteurs d'éléments personnalisés de remplacer une méthode intégrée telle que setAttribute .

Pouvez-vous fournir une bonne raison pour laquelle?

Cela ressemble à un hareng rouge. "Convaincre tous ceux qui écrivent des éléments personnalisés d'attacher une méthode avec un comportement spécifique à leur élément" n'est pas une solution à ce problème, qui concerne la définition des propriétés sur les éléments DOM.

@trusktr

Pouvez-vous fournir une bonne raison pour laquelle?

Les composants Web sont un standard. Ainsi, les dérivés de la norme devraient également être conformes à la norme.

Cependant, le remplacement de setAttribute ne correspond pas à cette condition : il crée un hack uniquement pour React alors qu'il existe de nombreux autres frameworks qui fonctionnent avec des composants Web prêts à l'emploi. Ainsi, ils devraient utiliser le hack React même s'ils ne fonctionnent pas du tout avec React. Je ne pense pas que ce soit la bonne solution.

Ensuite, il est bien connu que patcher les méthodes standard est une mauvaise approche. Le remplacement de setAttribute modifie le comportement d'origine qui peut rendre les utilisateurs finaux confus lorsqu'ils essaient de l'utiliser. Tout le monde s'attend à ce que standard fonctionne comme standard, et l'élément personnalisé ne fait pas exception car il hérite du comportement HTMLElement . Et bien que cela puisse fonctionner avec React, cela crée un piège pour tous les autres utilisateurs. Par exemple, lorsque les composants Web sont utilisés sans framework, setAttribute peuvent être souvent appelés. Je doute que les développeurs d'éléments personnalisés acceptent de se lancer dans cette approche.

Personnellement, je pense qu'une sorte de wrapper React semble beaucoup plus prometteur.

Salut les amis, en attendant, j'ai créé un shim pour envelopper votre composant web dans React https://www.npmjs.com/package/reactify-wc

import React from "react";
import reactifyWc from "reactify-wc";

// Import your web component. This one defines a tag called 'vaadin-button'
import "@vaadin/vaadin-button";

const onClick = () => console.log('hello world');

const VaadinButton = reactifyWc("vaadin-button");

export const MyReactComponent = () => (
  <>
    <h1>Hello world</h1>
    <VaadinButton onClick={onClick}>
      Click me!
    </VaadinButton>
  </>
)

J'espère que cela s'avère utile

(C'est ma première incursion dans OSS, et l'un des premiers open source de quelque chose hors de mon bureau. les critiques constructives sont plus que bienvenues 😄 )

Salut les amis, en attendant, j'ai créé un shim pour envelopper votre composant web dans React https://www.npmjs.com/package/reactify-wc

import React from "react";
import reactifyWc from "reactify-wc";

// Import your web component. This one defines a tag called 'vaadin-button'
import "@vaadin/vaadin-button";

const onClick = () => console.log('hello world');

const VaadinButton = reactifyWc("vaadin-button");

export const MyReactComponent = () => (
  <>
    <h1>Hello world</h1>
    <VaadinButton onClick={onClick}>
      Click me!
    </VaadinButton>
  </>
)

J'espère que cela s'avère utile

(C'est ma première incursion dans OSS, et l'un des premiers open source de quelque chose hors de mon bureau. les critiques constructives sont plus que bienvenues 😄 )

Super emballage :)

Il convient également de mentionner que la création de composants Web avec Stencil résout ce problème avec React : https://stenciljs.com/docs/faq#can -data-be-passed-to-web-components-

@matsgm Je suis heureux que la construction avec Stencil ait fonctionné pour vous. Cependant, à titre de mise en garde, dans notre expérience, Stencil s'est avéré ne pas fonctionner correctement avec d'autres frameworks de composants Web, en particulier Polymer, et nous avons été ennuyés par une demi-douzaine d'autres problèmes entre leurs outils de construction, leur support et leurs fonctionnalités générales. Votre kilométrage peut varier :)

J'essaie juste d'être à jour avec ce fil. Quelle est la solution finale ?

D'une certaine manière, je suis un grand fan de la définition explicite d'attr, d'accessoires, d'événements plutôt que d'heuristiques magiques qui peuvent être très déroutantes.

Par exemple, snabbdom utilise un onClick => click est toujours un succès perf.

<input attrs={{placeholder: `heyo`}} style={{color: `inherit`}} class={{hello: true, world: false}} on={{click: this.handleClick}} props={{value: `blah`}} />
attr = (attr, val) => elem.setAttribute(attr, val);
prop = (prop, val) => elem[prop] = val;
on  = (event, handler) => elem.addEventListener(event, handler)
style = (prop, val) => elem.style[prop] = val;
class = (name, isSet) => isSet ? elem.classList.add(name) : elem.classList.remove(val)
dataset = (key, val) => elem.dataset[key] = val;

Je souhaite que JSX prenne en charge l'espacement des noms avec une syntaxe à points. Cela signifie que les accessoires seraient l'espace de noms par défaut et que nous pourrions simplement écrire

<div tabIndex={-1} attr.title={"abcd"} on.click={handler} style.opacity={1} class.world={true} />`

pour info @sebmarkbage ^

const whatever = 'Whatever';
const obj = { a: 1, b: 2 };
const reactComponent = (props) => (
<div>
  ...
  <custom-element attr="{whatever}" someProp={obj} />
  { /* double quotes for attributes */ }
  { /* no quotes for properties */ }
</div>
);

Ceci est réalisable en modifiant l'analyseur React JSX Pragma.

Une option supplémentaire consiste à conserver propeties (ou props pour les fans de réaction) comme mot désigné pour le passage de propriété

Étant donné que la version 17.0 est sortie aujourd'hui, pourrions-nous mettre à jour ce problème pour refléter le statut du moment où cela sera résolu ?

Oui. Nous avions initialement prévu que React 17 soit une version avec beaucoup plus de dépréciations supprimées (et avec de nouvelles fonctionnalités). Cependant, il n'est pas très réaliste pour les anciennes bases de code de mettre à jour tout leur code, et c'est pourquoi nous avons décidé de sortir React 17 qui ne se concentre que sur un seul changement significatif (les événements sont attachés à l'élément racine plutôt qu'au document ). C'est pour que les anciennes applications puissent rester sur React 17 pour toujours et ne mettre à jour que certaines parties d'entre elles vers 18+.

Je sais que c'est frustrant que nous ayons dit que cette fonctionnalité particulière entrerait dans 17, mais vous remarquerez à peu près toutes les choses que nous avions initialement prévues pour 17 ont également été déplacées à 18. 17 est une version spéciale tremplin. Ce qui nous permet de faire des changements de rupture plus agressifs dans 18. Y compris potentiellement celui-ci s'il existe une voie à suivre claire.

Je ne sais pas quel est le dernier consensus de la communauté WC sur laquelle de ces options est préférable. Il est cependant très utile de les avoir tous écrits (grands accessoires à @robdodson pour avoir fait ce travail). Je suis curieux de savoir si les opinions des gens sur ces options ont évolué depuis la rédaction de ce fil, et s'il y a de nouvelles informations qui pourraient nous aider à choisir la direction.

Je ne pense pas que la communauté WC ait changé son option préférée, qui est toujours l'option 3. @developit peut en dire plus sur la compatibilité de Preact avec les éléments personnalisés, ce qui pourrait également être intéressant pour React. Pour un aperçu général de la compatibilité des frameworks avec la transmission de données (complexes) dans des éléments personnalisés, https://custom-elements-everywhere.com/ contient tous les détails.

Notez que dans https://github.com/vuejs/vue/issues/7582 , Vue a choisi d'utiliser un sceau, et ils ont choisi "." comme préfixe (pas le "@" de Glimmer).

Dans https://github.com/vuejs/vue/issues/7582#issuecomment -362943450, @trusktr a suggéré que l'implémentation SSR la plus correcte serait de ne pas rendre les propriétés sigil'd en tant qu'attributs dans le SSR'd HTML, et de à la place, définissez-les comme propriétés via JS pendant l'hydratation.

Je pense qu'il est assez peu probable que nous introduisions une nouvelle syntaxe JSX pour cette seule fonctionnalité particulière.

Il y a aussi la question de savoir s'il vaut la peine de faire l'option (3) si une version plus générique de l'option (5) est éventuellement sur la table. C'est-à-dire que l'option (5) pourrait être un mécanisme de bas niveau pour déclarer des nœuds React de bas niveau personnalisés avec un comportement de montage/mise à jour/démontage/hydratation personnalisé. Même pas spécifique aux éléments personnalisés en soi (bien qu'ils soient l'un des cas d'utilisation).

Plutôt que d'introduire une toute nouvelle syntaxe JSX pour cette fonctionnalité spécifique, qu'en est-il de l'introduction d'un JSX plus général et de son utilisation pour définir des propriétés personnalisées ?

J'ai proposé d'ajouter la syntaxe des propriétés calculées d'ECMAScript à JSX il y a longtemps (facebook/jsx#108) et je pense que ce serait un ajout utile à la syntaxe en général.

Si la syntaxe des propriétés calculées était disponible, cela laisserait ouverte la possibilité de définir des propriétés à l'aide de la syntaxe des propriétés calculées et des symboles ou des chaînes préfixées.

Par exemple:

import {property} from 'react';
// ...
<custom-img [property('src')]="corgi.jpg" [property('hiResSrc')]="[email protected]" width="100%">

@dantman Comment cette proposition gère-t-elle le rendu et l'hydratation du serveur ?

Je ne pense pas que quiconque dans la communauté WC veuille l'option 5.

Ce n'est vraiment pas différent de la pratique actuelle consistant à corriger l'écriture d'un wrapper React personnalisé pour les éléments personnalisés. L'inconvénient majeur de cette approche est qu'elle impose un fardeau à l'auteur du composant pour React dans un cas particulier ou au consommateur de composants pour des éléments personnalisés dans un cas particulier, ce qui ne devrait pas être nécessaire.

Que pensez-vous du rendu et de l'hydratation du serveur ? S'il n'y a pas de configuration explicite, quelle heuristique est souhaitable ? Y a-t-il eu une consolidation dans la façon dont cela est habituellement fait dans la communauté WC ? J'ai relu cette RFC et elle ne semble pas entrer dans les détails sur ce sujet. Mais c'est assez crucial, d'autant plus que React met davantage l'accent sur le rendu du serveur (car il est souvent critiqué à juste titre pour des valeurs par défaut trop centrées sur le client).

@gaearon Je ne connais pas assez l'hydratation et les propriétés personnalisées pour savoir ce qui est requis, il s'agit principalement d'une proposition de syntaxe.

L'idée générale est que property(propertyName) pourrait, selon la façon dont vous souhaitez l'implémenter, produire une chaîne préfixée (par exemple '__REACT_INTERNAL_PROP__$' + propertyName ) ou créer un symbole et enregistrer une association "symbol => propertyName" dans une carte.

Ce dernier peut être problématique si vous devez communiquer cette carte au client.

Cependant, à ma connaissance, les propriétés ne sont pas quelque chose que vous pouvez gérer sur le serveur et l'hydratation implique le code client, donc je ne sais pas quel est le plan pour cela. Quant à ma proposition, elle peut probablement être adaptée à tous les plans que vous avez pour résoudre ce problème. S'il y a quelque chose que vous prévoyez de faire réagir avec les propriétés sur le serveur, il peut simplement le faire lorsqu'il voit l'une des propriétés créées par property .

En tant qu'auteur de bibliothèque de composants Web, l'option 5 n'est pas un choix pratique. Je souhaite créer des éléments personnalisés sans avoir à définir explicitement un schéma pour chaque framework et composant. Et de nombreux auteurs d'éléments personnalisés ne le feront tout simplement pas, faisant peser la charge sur le développeur React.

Personne ne gagne avec l'option 5. 😕

@claviska Avez-vous des opinions sur la façon dont le rendu et l'hydratation du serveur devraient fonctionner avec des approches plus implicites ?

@gaearon SSR se subdivisera en deux situations : celles où l'élément personnalisé prend en charge SSR et celles où il ne le fait pas.

Pour les éléments qui ne prennent pas en charge SSR, la définition des propriétés sur le serveur n'a pas d'importance. À l'exception des propriétés de réflexion intégrées telles que id et className , elles peuvent être supprimées et écrites uniquement sur le client.

Pour les éléments qui prennent en charge les propriétés SSR, les propriétés seront importantes, mais uniquement pour qu'elles puissent déclencher le résultat qu'elles provoquent sur DOM. Cela nécessite une instance d'élément ou une sorte de proxy/remplaçant SSR, et un protocole pour communiquer l'état du DOM SSR. Il n'y a pas encore d'interface côté serveur commune pour ce processus, donc il n'y a vraiment rien à faire ici pour le moment. Les auteurs de composants Web et les mainteneurs de bibliothèques devront comprendre certaines choses avant qu'il ne soit viable pour React de construire des ponts là-bas.

Dans le travail de mon équipe sur SSR, nous avons intégré React en patchant createElement et en utilisant dangerouslySetInnerHTML . Je pense que c'est le niveau d'intégration/expérimentation auquel nous serons un peu. À un moment donné, j'espère que nous pourrons converger vers certains protocoles utilisateur pour l'interopérabilité au sein des systèmes SSR. Jusque-là, il est parfaitement sûr et prudent d'avoir une propriété d'élément personnalisée et un support d'événement sans _deep_ SSR. La balise de l'élément serait toujours SSR en tant qu'enfant d'un composant React comme c'est le cas aujourd'hui.

À l'exception des propriétés de réflexion intégrées telles que id et className, elles peuvent être supprimées et écrites uniquement sur le client.

Pouvez-vous m'aider à comprendre comment cela fonctionne? Par exemple, disons qu'il y a <github-icon iconname="smiley" /> . Voulez-vous dire que le SSR devrait simplement inclure <github-icon /> dans la réponse HTML, puis React définira domNode.iconname = ... pendant l'hydratation ? Dans ce cas, je ne suis pas sûr de comprendre ce qui se passe si l'implémentation de l'élément personnalisé se charge avant que l'hydratation React ne se produise. Comment l'implémentation de github-icon saura-t-elle quelle icône afficher si iconname n'existe pas dans le code HTML ?

Il n'y a pas encore d'interface côté serveur commune pour ce processus, donc il n'y a vraiment rien à faire ici pour le moment. Les auteurs de composants Web et les mainteneurs de bibliothèques devront comprendre certaines choses avant qu'il ne soit viable pour React de construire des ponts là-bas.

Je suis curieux de savoir s'il y a quelque chose en particulier qui devrait arriver pour que la communauté forme un consensus ici. Est-ce que React le retient? Je comprends très bien la frustration liée au fait que ce dossier soit ouvert depuis 2017. Par contre, cela fait trois ans, et je pense que vous dites que ce consensus ne s'est pas encore formé. Quelles sont les conditions préalables pour que cela se produise ? Est-ce juste une question de plus d'expérimentation?

@gaearon si l'implémentation de l'élément personnalisé se charge avant que l'hydratation React ne se produise, il attribuera la valeur par défaut de l'attribut iconname . Qu'entendez-vous par if _iconname_ does not exist in the HTML ? Si le type HTMLElement n'a pas d'attribut défini, il l'ignorera. une fois la définition de l'élément personnalisé chargée, elle étendra le type HTMLElement et définira iconname et pourra réagir à une nouvelle valeur transmise.

J'essaie de comprendre cela du point de vue d'un utilisateur. Nous rendons une page sur le serveur. Il a une icône au milieu d'un texte. Cette icône est implémentée en tant qu'élément personnalisé. Quelle est la séquence de choses que l'utilisateur final devrait expérimenter ?

Ma compréhension jusqu'à présent est que:

  1. Ils voient le résultat de rendu initial. Il inclura tout le balisage normal, mais ils ne verront pas du tout l'élément personnalisé <github-icon> . Vraisemblablement, ce serait un trou, comme un div vide. Corrigez-moi si j'ai tort, s'il-vous plait (?). Son implémentation n'a pas encore été chargée.

  2. L'implémentation de l'élément personnalisé <github-icon> charge et est enregistrée. Si je comprends bien, c'est le processus appelé "mise à niveau". Cependant, même si son JS est prêt, il ne peut toujours rien afficher car nous n'avons pas inclus le iconname dans le HTML. Nous n'avons donc pas d'informations sur l'icône à afficher. C'est parce que nous avons dit plus tôt que "les propriétés non intégrées peuvent être supprimées du HTML". Ainsi, l'utilisateur voit toujours un "trou".

  3. React et le code de l'application se charge. L'hydratation se produit. Pendant l'hydratation, React définit la propriété .iconname = selon l'heuristique. L'utilisateur peut voir l'icône maintenant.

Est-ce une répartition correcte pour le cas où l'implémentation JS de l'élément personnalisé se charge en premier ? Est-ce le comportement souhaitable pour la communauté WC ?

@gaearon oui, c'est ce à quoi je m'attendrais dans cette situation.

D'un autre côté, cela fait trois ans, et je pense que vous dites que ce consensus ne s'est pas encore formé

Je ne dis pas que le consensus n'a pas été atteint. Je dis que vous n'avez pas à vous soucier de la SSR _deep_ pour le moment, et que vous ne devriez pas laisser des inquiétudes assez vagues à son sujet bloquer l'interopérabilité côté client la plus basique qui peut être accomplie et être extrêmement utile en ce moment.

Je n'ai jamais vu cette distinction auparavant (profond vs peu profond), alors laissez-moi essayer de la reformuler pour vérifier si je vous ai bien compris.

Par SSR "profond", je pense que vous entendez SSR similaire à la façon dont cela fonctionne dans les composants React. C'est-à-dire que le rendu d'un CE produirait une sortie qui peut être affichée avant que la logique de ce CE ne soit chargée. C'est quelque chose que vous dites que nous ne devrions

Par SSR "pas profond", je pense que vous dites que nous venons de mettre la balise CE elle-même en HTML. Sans se soucier de ce à quoi il se résout. Cela a du sens pour moi aussi. Ma question portait cependant sur _ce_ flux - pas sur le SSR "profond".

En particulier, https://github.com/facebook/react/issues/11347#issuecomment -713230572 décrit une situation où même lorsque nous avons _déjà_ téléchargé la logique CE, nous ne pouvons toujours rien montrer à l'utilisateur jusqu'à l'hydratation. Devient ses propriétés sont inconnues jusqu'à ce que le code client JS de l'ensemble de l'application se charge et que l'hydratation se produise. Je demande si c'est bien le comportement souhaité.

Je ne pense pas que ce soit une vague préoccupation de mon point de vue. Je ne veux pas mal comprendre la demande de fonctionnalité. Donc, obtenir la sémantique exacte spécifiée m'aiderait à évaluer cette proposition. Par exemple, une préoccupation pratique est que React ne différencie pas réellement les attributs/propriétés de la production pendant l'hydratation aujourd'hui, car aucun des composants intégrés n'en a besoin. Mais c'est quelque chose que nous pourrions ajouter pour ce cas particulier si c'est vraiment nécessaire.

@gaearon

Je pense que https://github.com/facebook/react/issues/11347#issuecomment -713230572 n'est peut-être pas le meilleur exemple dans lequel cette fonctionnalité est nécessaire.

Au moins d'après mon expérience, définir des propriétés plutôt que des attributs n'est pas tout à fait nécessaire pour des types plus simples comme des chaînes, des nombres ou des booléens.

Le cas que vous décrivez pourrait facilement être réalisé en utilisant directement iconname comme attribut et ce serait toujours en grande partie le même résultat final sans attendre l'hydratation.

Lesl'implémentation de l'élément personnalisé se charge et il est enregistré. Si je comprends bien, c'est le processus appelé "mise à niveau". Cependant, même si son JS est prêt, il ne peut toujours rien afficher car nous n'avons pas inclus le nom de l'icône dans le code HTML. Nous n'avons donc pas d'informations sur l'icône à afficher. C'est parce que nous avons dit plus tôt que "les propriétés non intégrées peuvent être supprimées du HTML". Ainsi, l'utilisateur voit toujours un "trou".

Quant à ce que vous mentionnez ici, selon la façon dont le composant est implémenté, vous pourriez éviter ce problème de voir un "trou".

Cela pourrait être réalisé soit en définissant simplement des valeurs par défaut dans ce cas, soit en acceptant une partie ou même la plupart du contenu via des emplacements afin que le contenu soit affiché avant même que le composant ne soit mis à niveau.


Je pense que cette fonctionnalité serait surtout utile pour utiliser des composants plus complexes qui ont des propriétés qui sont des objets, des tableaux, des fonctions, etc.

Prenons par exemple le composant de défilement virtuel lit-virtualizer .

Pour que cela fonctionne correctement, il faut un tableau items et une fonction renderItem et vous pouvez même éventuellement définir un élément scrollTarget , qui ne peut être défini que dans React en utilisant refs à l'heure actuelle.

Pour un cas comme celui-ci, vous chargeriez probablement le contenu avec une sorte de pagination ou de chargement paresseux de toute façon, donc ne pas avoir de contenu jusqu'à l'étape d'hydratation sur un cas SSR pourrait ne pas être si problématique.

@gaearon Veuillez noter que les éléments personnalisés sont une norme DOM et qu'il n'y a donc pas en soi de "communauté WC" dans le sens où tous ceux qui utilisent des éléments personnalisés les utilisent de la même manière. Les éléments personnalisés sont intégrés à leur manière pour chaque développeur, car ils sont intrinsèquement une primitive de bas niveau sur laquelle les gens s'appuient.

Cela dit, au fil du temps, de nombreux modèles ont émergé et tous les frameworks populaires (à l'exception de React) implémentent une solution pour assurer la compatibilité avec cette norme DOM. Comme vous pouvez le voir sur https://custom-elements-everywhere.com/ , tous les frameworks/libraries (sauf React) ont choisi des options différentes. Dans ce numéro, les options choisies sont répertoriées comme options 1, 2 et 3. Il n'y a pas de framework/bibliothèque qui implémente actuellement l'option 4 ou 5 et je ne pense pas qu'il soit souhaitable de les implémenter.

Par conséquent, je propose que React emboîte le pas aux autres frameworks/bibliothèques et choisisse une option qui a fait ses preuves, par exemple parmi les options 1-3. Je ne pense pas que React doive réinventer la roue ici en choisissant l'option 4 ou 5, pour laquelle nous n'avons aucune donnée pour laquelle est une solution maintenable à long terme pour React ou ceux qui s'appuient sur les normes DOM.

Donc, par le processus d'élimination (avec les commentaires liés ci-dessous), puisque l'option 3 ne sera pas appliquée à JSX et que les options 4 et 5 ont été marquées comme indésirables par les membres de WC dans ce fil, nous sommes coincés avec 1 ou 2 .

Et puisque 1 semble avoir des inconvénients intenables, il semble que l' option 2 soit la bonne ? Allez-y simplement avec la façon dont Preact le fait?


Il n'y a pas de framework/bibliothèque qui implémente actuellement l'option 4 ou 5 et je ne pense pas qu'il soit souhaitable de les implémenter.

( @TimvdLippe dans https://github.com/facebook/react/issues/11347#issuecomment-713474037)

Je pense qu'il est assez peu probable que nous introduisions une nouvelle syntaxe JSX pour cette seule fonctionnalité particulière.

( @gaearon dans https://github.com/facebook/react/issues/11347#issuecomment-713210204)

Cela aurait du sens pour moi oui. Merci pour le résumé ! ??

Au moins d'après mon expérience, définir des propriétés plutôt que des attributs n'est pas tout à fait nécessaire pour des types plus simples comme des chaînes, des nombres ou des booléens. Le cas que vous décrivez pourrait facilement être réalisé en utilisant directement iconname comme attribut et ce serait toujours en grande partie le même résultat final rendu sans attendre l'hydratation.

Je ne suis pas tout à fait. Mon scénario hypothétique était une réponse à https://github.com/facebook/react/issues/11347#issuecomment -713223628 qui ressemblait à une suggestion selon laquelle nous ne devrions pas du tout émettre d'attributs dans le code HTML généré par le serveur, à l'exception de id et class . Je ne sais pas ce que signifie "utiliser simplement iconname comme attribut" - êtes-vous en train de dire que vous aimeriez voir le HTML généré par le serveur _include_ d'autres attributs ? Cela semble contredire ce qui vient d'être dit plus tôt. Je pense que cela aiderait vraiment si chaque réponse présentait une image complète car sinon nous allons continuer à tourner en rond. Ma question pour vous est :

  1. Qu'est-ce qui est exactement sérialisé dans le code HTML généré par le serveur dans le scénario que j'ai décrit
    une. Au cas où <github-icon iconname="smiley" />
    b. Dans le cas que vous avez évoqué, par exemple <github-icon icon={{ name: "smiley" }} />
  2. Quelle API DOM est appelée sur le client pendant l'hydratation dans l'un ou l'autre de ces cas

Merci!

Était-il considéré comme une option de créer une cale, en remplaçant React.createElement() par une implémentation personnalisée, qui mappe les propriétés à l'intérieur ?

Exemple d'utilisation :

/** <strong i="8">@jsx</strong> h */
import { h } from 'react-wc-jsx-shim';

function Demo({ items }) {
   return <my-custom-list items={items} />
}

Projet de mise en œuvre :

export function h(element, props, children) {
   if(typeof element === 'string' && element.includes('-')) {
      return React.createElement(Wrapper, { props, customElementName: element }, children)
   }
   return React.createElement(element, props, children);

}

function Wrapper({customElementName, props}) {
   const ref = React.useRef();
   React.useEffect(() => {
      for(const prop in props) {
         ref.current[prop] = props[prop];
      }
   });
   return React.createElement(customElementName, { ref, ...props });
}

Je sais que ce n'est pas une fonctionnalité intégrée, mais devrait débloquer les utilisateurs avec une surcharge très faible, je pense.

@just-boris c'est essentiellement ce que font les applications ou les bibliothèques maintenant, avec un patch createElement ou un wrapper. Vous devez également exclure les noms de propriétés à casse spéciale dans React. Je ne pense pas qu'il y ait une liste exportée, alors ils codent juste en dur une liste de refus comme ['children', 'localName', 'ref', 'elementRef', 'style', 'className'] .

Parce qu'il est basé sur les références, les propriétés ne sont pas définies sur le serveur. C'est pourquoi j'appelle la préoccupation vague. Il n'y a aucun moyen de définir des propriétés dans React, donc tout est cassé pour le moment. La solution de contournement disponible ne fonctionne déjà que sur le client. S'il devient intégré un moyen de définir des propriétés sur le client et non sur le serveur, cela ne répare ou ne casse pas soudainement le SSR.

Je serais en fait plus concerné par les événements que par la RSS. React n'a pas de moyen d'ajouter des gestionnaires d'événements, et c'est un énorme trou dans l'interopérabilité avec les API DOM de base.

Était-il considéré comme une option de créer une cale, remplaçant React.createElement() par une implémentation personnalisée, qui mappe les propriétés à l'intérieur ?

Comme mentionné par Rob dans le numéro d'origine, j'ai expérimenté cela dans le passé via https://github.com/skatejs/val , donc avoir un pragma JSX personnalisé qui enveloppe React.createElement est une solution viable - peut-être solution a court terme.

J'ai également la branche WIP de Skate qui implémente à la fois ce que l'on appelle le SSR profond et peu profond, en particulier pour React. Les enseignements tirés de ce travail jusqu'à présent sont que vous ne pouvez pas avoir un seul wrapper JSX pour toutes les bibliothèques si vous souhaitez effectuer une SSR approfondie, car chaque bibliothèque dépend de ses propres API et algorithmes pour le faire. (Pour le contexte, Skate a un élément personnalisé de base dans sa bibliothèque et de petites couches conçues pour intégrer chaque bibliothèque populaire sur le marché afin de rendre la consommation et l'utilisation cohérentes à tous les niveaux.)


Je n'opère plus beaucoup en open source, et ne fais plus Twitter non plus, alors n'hésitez pas à prendre mon avis avec un grain de sel.

L'option 1 semble exister en tant qu'avocat du diable pour l'option 2.

L'option 2 semble être la voie à suivre. C'est proche de ce que Preact a fait, il y a donc un précédent et une possibilité d'une norme communautaire (ce qui, je pense, serait fantastique de toute façon ; JSX a montré que cela peut fonctionner), et c'est aussi un geste pragmatique pour React en étant facile à mettre à niveau (par opposition à l'option 1).

L'option 3 a l'air bien sur le papier, mais je pense que sur le plan logistique, ce serait beaucoup plus difficile que l'option 2 en raison des frais généraux cognitifs, de la documentation autour de ces frais généraux et des inconnues avec la SSR.

Comme mentionné, j'avais initialement présenté l'option 4 mais après réflexion, je ne suis plus sûr de l'aimer. Vous pouvez le changer pour qu'il soit plus opt-in, où vous spécifiez props et events explicites (au lieu de attrs ), mais même dans ce cas, l'option 2 nécessite moins de connaissances sur les détails de l'implémentation et personne n'a rien à changer pour l'adopter.

L'option 5 donne l'impression que nous ignorerions complètement le problème central.


Soit dit en passant, je suis super content de voir que ça bouge et j'espère que vous allez bien !

S'il y a une solution possible dans l'userland, peut-elle être consolidée et recommandée par la communauté pour l'instant ?

Il serait beaucoup plus facile de convaincre l'équipe principale de React d'ajouter une fonctionnalité si elle existe déjà en tant que module tiers populaire et viable.

S'il y a une solution possible dans l'userland, peut-elle être consolidée et recommandée par la communauté pour l'instant ?

Je ne pense pas que ce soit quelque chose qui puisse être [soigneusement] corrigé de l'extérieur. La plupart des solutions utilisateur sont des wrappers tels que this et this . Sans modifier React, je ne peux pas penser à une alternative raisonnable à cette approche, et le wrapping n'est certainement pas une solution idéale. ??

Notez que dans vuejs/vue#7582, Vue a choisi d'utiliser un sceau, et ils ont choisi "." comme préfixe (pas le "@" de Glimmer).

Je n'ai pas de lien vers une déclaration faisant autorité à ce sujet (et peut-être qu'un proche de Vue peut le confirmer explicitement), mais il semble que Vue soit passé à l'option 2 à partir de Vue 3.0, ce qui signifie qu'il se comporte de la même manière que Preact. (Voir ce numéro pour le contexte.)

Parce qu'il est basé sur les références, les propriétés ne sont pas définies sur le serveur. C'est pourquoi j'appelle la préoccupation vague. Il n'y a aucun moyen de définir des propriétés dans React, donc tout est cassé pour le moment. La solution de contournement disponible ne fonctionne déjà que sur le client. S'il devient intégré un moyen de définir des propriétés sur le client et non sur le serveur, cela ne répare ou ne casse pas soudainement le SSR.

Pouvez-vous s'il vous plaît répondre aux questions spécifiques dans https://github.com/facebook/react/issues/11347#issuecomment -713514984 ? J'ai l'impression qu'on se parle. Je veux juste comprendre l'ensemble du comportement prévu - d'une manière spécifiée. Qu'est-ce qui est défini quand et qu'est-ce qui est sérialisé dans ce cas.

En tant que développeur Preact + WC aléatoire qui a trébuché ici, je voulais souligner que "l'option 2" n'est pas seulement un _changement révolutionnaire_, mais aussi une _solution incomplète_ (Preact ne peut toujours interagir de manière déclarative qu'avec un sous -

Même en tant qu'utilisateur actuel de l'heuristique, cela semble difficilement souhaitable à long terme.


L'heuristique de Preact ("option 2") est incapable de définir des propriétés qui ont une signification particulière dans React (clé, enfants, etc.) ou Preact (tout ce qui commence par on ).

C'est-à-dire après le rendu sous JSX :

<web-component key={'key'} ongoing={true} children={[1, 2]} />

les propriétés key , ongoing et children seront toutes undefined (ou quelle que soit la valeur par défaut) sur l'instance créée de web-component .

De plus, contrairement à l'OP, cette option est également un changement décisif. La plupart des composants Web (par exemple créés avec lit-element ) offrent la possibilité de transmettre des données riches sérialisées via des attributs, dans le but d'être utilisés avec du HTML pur. De tels composants peuvent être rendus à partir de React comme si

<web-component richdata={JSON.stringify(something)} />

qui se brisera si "l'option 2" est choisie. Je ne sais pas à quel point une telle utilisation de composants Web est courante dans React, mais il existe certainement des bases de code qui le font, c'est moins efficace que les références mais aussi plus rapide.

Enfin, l'auteur du composant Web est libre d'attacher une sémantique subtilement différente à l'attribut et à la propriété du même nom. Un exemple serait un composant Web imitant le comportement de l'élément natif input - en traitant l'attribut value comme valeur initiale et en observant uniquement les changements sur la propriété value . Ces composants ne peuvent pas être pleinement interagis via jsx avec l'approche heuristique, et pourraient également subir une rupture subtile s'il était introduit.

Je pense qu'il est assez peu probable que nous introduisions une nouvelle syntaxe JSX pour cette seule fonctionnalité particulière.

Qu'en est-il de l'utilisation des espaces de noms ? https://github.com/facebook/jsx/issues/66 propose d'ajouter un espace react noms key et ref . Dans le même ordre d'idées, est-ce que quelque chose comme react-dom serait un espace de noms correct à créer pour identifier les propriétés DOM ? En utilisant l'exemple de l'option 3 de l'OP, ce serait :

<custom-img react-dom:src="corgi.jpg" react-dom:hiResSrc="[email protected]" width="100%">

Ce n'est pas le plus ergonomique. Juste dom serait mieux, mais je ne sais pas si React veut créer des espaces de noms qui ne commencent pas par react . De plus, une mauvaise ergonomie à ce sujet n'est pas la fin du monde, si l'idée est que la définition en tant que propriétés devrait être le cas le moins courant. L'utilisation d'attributs est préférable, car cela assure la parité avec le SSR. La définition en tant que propriétés est utilisée lorsque les attributs sont problématiques (par exemple, pour passer un objet ou pour des propriétés non soutenues par des attributs), et c'est précisément pour ces propriétés problématiques que nous ne pouvons pas les SSR aux attributs de toute façon, et aurions besoin de les hydrater via JS.

une _solution incomplète_

(Preact ne peut toujours interagir de manière déclarative qu'avec un sous -

@brainlessbadger pouvez-vous également modifier votre commentaire ci-dessus pour inclure ce qu'est réellement ce sous-ensemble ? Par exemple. quels composants Web/éléments personnalisés sont concernés (avec exemples) ? Et comment exactement sont-ils affectés?

@karlhorky j'ai ajouté une explication. Désolé d'être inutilement vague.

Concernant les événements de composants Web. Existe-t-il encore un consensus sur la manière d'éviter un blocage futur par des noms d'événements en collision ?

C'est également un problème pour les attributs et les propriétés, car de nouveaux attributs peuvent être ajoutés à HTMLElement comme tous ceux-ci : https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes ceux qui viennent d'être ajoutés aussi, donc il y en a encore de nouveaux qui sont ajoutés.

Je pense qu'à strictement parler, vous devriez utiliser un - au nom de tout attribut personnalisé ou événement personnalisé.

Tout le reste est un compromis pesant les risques que ce composant empêche une future spécification d'utiliser un joli nom.

Les événements me rendent encore plus nerveux, car ce n'est pas seulement un risque que ce composant ait un comportement incompatible. Si le nouvel événement apparaît, il déclenchera tous les composants Web existants dans ce chemin d'arborescence.

Si des attributs et des événements personnalisés devaient utiliser un - dans leur nom, cela pourrait être utilisé comme heuristique pour déterminer ce qu'il faut utiliser. Ensuite, nous aurions un cas spécial pour ceux intégrés. C'est déjà ainsi que nous savons s'il s'agit d'un élément personnalisé en premier lieu.

Les propriétés pourraient alors être utilisées comme commodité pour fournir une main courte plus agréable. Au moins, ceux-ci peuvent masquer les propriétés intégrées et ne pas entrer en collision avec les propriétés futures.

Je pense qu'à strictement parler, vous devriez avoir à utiliser un - au nom de tout attribut personnalisé ou événement personnalisé.

La spécification de l'élément personnalisé n'inclut pas de telles restrictions, donc leur application dans React affectera considérablement l'interopérabilité.

Il ne faut pas s'attendre à ce que les auteurs d'éléments personnalisés souscrivent aux exigences arbitraires d'un framework. Imaginez chaque framework faisant cela avec des opinions et des conventions différentes. Cela va à l'encontre du but de l'utilisation de composants Web. ??

Idéalement, le cadre devrait s'adapter pour s'adapter à la norme.

En effet. setAttribute() et dispatchEvent() sont que des API non spécifiques aux composants Web qui ont autorisé des noms arbitraires depuis leur création. N'importe quel élément peut avoir n'importe quel attribut et déclencher n'importe quel événement - c'est juste une vérité fondamentale du DOM.

L'espace data- noms

Je ne suggérerais pas que React ajoute cela, mais que la communauté adopte cette convention pour aider à protéger le futur espace de noms. Je trouve juste dommage que ce problème ne soit pas pris au sérieux. Mais peut-être que le Web est voué à n'ajouter que des noms gênants aux nouvelles API.

J'aime l'idée d'exclure simplement les non-builtins de SSR et de préférer les propriétés pendant l'hydratation car avec le shadow DOM déclaratif, le contenu réel pourrait être étendu à l'intérieur.

Concrètement, je pense que cela posera problème pour les approches actuelles que les gens utilisent avec SSR + AMP car cela utilise des attributs et vous pouvez supposer que le runtime AMP a déjà été chargé.

En supposant que la porte ne soit pas fermée sur l'option 3, elle présente un avantage significatif par rapport à l'option 2, non répertorié dans les inconvénients ci-dessus - Le problème que j'ai trouvé avec l'option 2 est que si les bibliothèques de composants Web sont chargées de manière asynchrone , preact ne savoir, pour un élément inconnu, qu'une "propriété" sera une propriété. Puisqu'il ne trouve pas la propriété existante dans l'élément inconnu, il utilise par défaut un attribut. Une solution consiste à ne pas afficher ce composant tant que les bibliothèques de composants Web dépendantes ne sont pas chargées, ce qui n'est pas très élégant (ou idéal en termes de performances). Étant donné que mon groupe utilise asp.net, pas node.js, je n'ai jamais vraiment exploré la SSR, donc les observations ci-dessous sont spéculatives.

C'est un beau geste, je pense, de la part de l'équipe React, de suggérer d'aller au-delà de ce que d'autres frameworks supportent (à ma connaissance), autant que SSR, permettant de transmettre les propriétés de l'objet lors du rendu initial, ce qui pourrait servir le l'utilisateur mieux.

Je ne sais pas si j'énonce l'évidence ou non, mais je pense qu'il existe un certain consensus avec les composants Web selon lequel pour certaines propriétés, il est pratique de définir parfois la valeur initiale de l'attribut via une chaîne JSON , entouré de guillemets simples.

AMP utilise une autre convention, cependant - il utilise le script type=application.json pour ce faire, ce qui est une alternative raisonnable (mais détaillée).

Mais s'en tenir à l'approche attributaire :

La bibliothèque de composants Web peut ensuite analyser l'attribut ou être transmise dans la valeur via une propriété.

Donc pour éviter les retards d'hydratation il serait pratique de régler :

<github-icon icon='{"name":"smiley"}'></github-icon>

pendant la RSS. Ensuite, github-icon pourrait immédiatement faire son travail, effectuer en interne une analyse JSON.parse de l'attribut et ne pas avoir à attendre que React transmette la valeur de l'objet (plus complète?).

Alors maintenant, nous avons une situation délicate - si le rendu sur le serveur, nous voulons que "l'icône" soit traitée comme un attribut, mais soit capable de transmettre la valeur en tant qu'objet, que idéalement le mécanisme SSR enchaînerait dans l'attribut. Sur le client, nous voulons pouvoir transmettre l'objet directement, et ne pas passer par la surcharge de la chaîne et de l'analyse.

Bien sûr, certaines propriétés sont des objets qui ne se prêtent pas à la chaîne/analyse, donc cela ne s'applique pas, et ces propriétés devraient attendre la fin de l'hydratation.

Si toutes les propriétés et attributs suivaient la convention de nommage préférée de l'équipe React (peut-être) - tous les attributs avaient des tirets et toutes les propriétés étaient des noms composés utilisant camelCase, peut-être que l'existence d'un tiret pourrait aider à distinguer les attributs des propriétés :

<github-icon my-icon={myStringifiedProperty} myIcon={myObjectProperty}></github-icon>

Le problème est que rien dans la syntaxe ci-dessus n'indique quoi faire sur le serveur par rapport au client, et idéalement, nous serions en mesure d'utiliser une liaison pour les deux, et React serait suffisamment intelligent pour déterminer laquelle utiliser.

React pourrait simplement supposer que si un attribut/clé de propriété est un cas de chameau, il doit toujours le transmettre en tant qu'objet côté client et une sérialisation JSON de l'objet s'il est défini pendant le SSR. De même, les tirets dans la clé pourraient (en toute sécurité, je pense) supposer qu'il s'agit d'un attribut et simplement passer le .toString() de la valeur. Mais je pense que cela suppose trop. Et ne pas prendre en charge les attributs et propriétés d'un seul mot, qui sont considérés comme du HTML valide si les attributs sont appliqués à un composant Web étendant HTMLElement, serait trop restreint. Je suis favorable à ce que le W3C publie une liste de noms d'attributs "réservés" qu'il pourrait utiliser à l'avenir, similaire aux mots clés réservés pour JS, et que les frameworks trouvent des moyens d'avertir les développeurs s'ils utilisent incorrectement un nom d'attribut réservé.

Mais je pense que l'option 3 est la meilleure approche. Cependant, s'il peut être amélioré, comme suggéré par @gaearon , pour une expérience utilisateur encore meilleure, ce serait formidable.

Ma suggestion serait :

<github-icon icon={myDynamicIcon}/>

signifie attribut (toString()).

<github-icon icon:={myDynamicIcon}/>

signifierait -- pendant SSR, ignorer, mais lier le client à la propriété de l'objet (après hydratation).

Maintenant, qu'en est-il du scénario (certains des ?) L'équipe React souhaite résoudre ? Ma première pensée était juste un autre sceau, comme deux deux-points :

<github-icon icon::={myDynamicIcon}/> //Not my final suggestion!

signifierait, pendant la SSR, JSON.stringify la propriété, définie en tant qu'attribut dans le code HTML rendu et transmis en tant qu'objet sur le client lorsque la propriété à laquelle il est lié change après l'hydratation.

Cela laisse la situation délicate de savoir quoi faire avec les noms composés. C'est-à-dire si on pose :

<github-icon iconProps::={myDynamicIconProp}/>  //Not my final suggestion!

il n'était pas controversé dans le passé que l'attribut correspondant pour le nom de propriété iconProps soit icon-props.

Cela est peut-être devenu plus controversé, en raison du spectre selon lequel certains de ces composants Web pourraient, sans modification, être intégrés à la plate-forme, où les attributs ne peuvent pas avoir de tirets (mais les propriétés peuvent être camelCase). A ma connaissance, il n'existe pas encore d'élément natif permettant de passer des objets complexes via la désérialisation JSON, mais je ne serais pas surpris si le besoin s'en faisait sentir dans le futur. Alors, comment React saurait-il quand insérer des tirets ou non ?

La seule suggestion (laide ?) que je puisse faire est :

<github-icon icon-props:iconProps={myDynamicIconProp}/>

ce qui signifie, sur le serveur, utiliser l'attribut icon-props après la sérialisation, et sur le client, utiliser la propriété iconProps, en passant directement les objets.

Un autre avantage potentiel (à long terme ?) de la notation plus détaillée, permettant une paire hybride attribut/propriété, est le suivant : il peut être un peu plus rapide de définir à plusieurs reprises les propriétés d'un élément natif, plutôt que l'attribut correspondant, en particulier pour propriétés qui ne sont pas des chaînes. Si tel est le cas, React utilise-t-il actuellement des attributs sur le serveur et des propriétés sur le client ? Si non, est-ce à cause du même problème de dénomination des difficultés de traduction, que cette notation résoudrait ?

@bahrus je pense que ça peut être simplifié

<my-element attr='using-quotes' property={thisIsAnObject} />

Je pense qu'il serait peut-être préférable d'illustrer le problème avec un exemple concret.

L'élément HTML Form a un certain nombre d'attributs. Ceux avec des "noms composés" ne semblent pas tous suivre un modèle de nommage cohérent - en général, il s'en tient aux minuscules, pas de séparateurs, mais parfois il y a un séparateur de tirets - "accept-charset" par exemple, parfois pas - - "novalider".

Le nom de propriété JS correspondant ne rentre pas non plus dans un joli modèle universel -- acceptCharset, noValidate. La propriété noValidate / l'attribut novalidate est un booléen, donc sur le client, ce serait du gaspillage (j'imagine) de faire myForm.setAttribute('novalidate', '') par opposition à myForm.noValidate = true.

Cependant, sur le serveur, cela n'a pas de sens de définir myForm.noValidate = true, car nous voulons envoyer une représentation sous forme de chaîne du DOM, nous devons donc utiliser l'attribut :

<form novalidate={shouldNotValidate}>

En fait, il n'est pas clair pour moi que React/JSX dispose d'un moyen universel de définir un attribut booléen de manière conditionnelle , en s'appuyant peut-être sur une table de recherche fixe et statique, qui semble (?) trop rigide . Si je peux être si audacieux, cela semble être un autre domaine que React/JSX pourrait améliorer, pour être entièrement compatible avec le fonctionnement (désordonné) du DOM, dans le cadre de cette RFC ?

Comment représenter tous ces scénarios, afin que le développeur ait un contrôle total sur le serveur (via les attributs) et le client (via les propriétés), sans sacrifier les performances ? Dans ce cas d'un booléen comme novalidate, je proposerais :

<form novalidate:noValidate?={shouldNotValidate}/>

Si React prenait entièrement en charge le DOM, aussi désordonné soit-il, de la manière la plus performante possible, je pense que cela contribuerait grandement à la prise en charge des éléments personnalisés, qui ne sont probablement pas non plus très cohérents dans les modèles de nommage.

Il me semble que l'ajout de la prise en charge de la sérialisation facultative des propriétés d'objet aux attributs via JSON.stringify sur le serveur serait un énorme gagnant-gagnant pour React et les composants Web.

Ou est-ce que j'ai raté quelque chose ? ( @eavichay , comment suggéreriez-vous que ces scénarios soient mieux représentés, sans suivre votre exemple).

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