React: RFC: Plano para atributos / propriedades de elementos personalizados no React 18

Criado em 24 out. 2017  ·  129Comentários  ·  Fonte: facebook/react

Isso se destina a abordar # 7249. O documento descreve os prós e os contras de várias abordagens que o React pode usar para lidar com atributos e propriedades em elementos personalizados.

TOC / Resumo

  • Fundo
  • Propostas

    • Opção 1: definir apenas propriedades

    • Prós



      • Fácil de entender / implementar


      • Evita conflito com atributos globais futuros


      • Aproveita a "atualização" do elemento personalizado


      • Elementos personalizados tratados como qualquer outro componente React



    • Contras



      • Possivelmente uma mudança significativa


      • Precisa de ref para definir o atributo


      • Não está claro como a renderização do lado do servidor funcionaria



    • Opção 2: Propriedades, se disponíveis

    • Prós



      • Mudança ininterrupta



    • Contras



      • Os desenvolvedores precisam entender a heurística


      • Retornar aos atributos pode entrar em conflito com futuros globais



    • Opção 3: diferencie as propriedades com um sigilo

    • Prós



      • Mudança ininterrupta que os desenvolvedores podem optar por aderir


      • Semelhante a como outras bibliotecas lidam com atributos / propriedades


      • O sistema é explícito



    • Contras



      • É uma nova sintaxe


      • Não está claro como a renderização do lado do servidor funcionaria



    • Opção 4: adicionar um objeto de atributos

    • Prós



      • O sistema é explícito


      • Estender a sintaxe também pode resolver problemas com o tratamento de eventos



    • Contras



      • É uma nova sintaxe


      • Pode ser uma mudança significativa


      • Pode ser uma mudança maior do que qualquer uma das propostas anteriores



    • Opção 5: uma API para consumir elementos personalizados

    • Prós



      • O sistema é explícito


      • Mudança ininterrupta


      • Idiomático para reagir



    • Contras



      • Pode ser muito trabalhoso para um componente complexo


      • Pode aumentar o tamanho do pacote


      • A configuração precisa acompanhar o ritmo do componente



Fundo

Quando o React tenta passar dados para um elemento personalizado, ele sempre o faz usando atributos HTML.

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

Como os atributos devem ser serializados em strings, essa abordagem cria problemas quando os dados transmitidos são um objeto ou array. Nesse cenário, terminamos com algo como:

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

A solução alternativa para isso é usar ref para definir manualmente a propriedade.

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

Essa solução alternativa parece um pouco desnecessária, pois a maioria dos elementos personalizados enviados hoje são escritos com bibliotecas que geram automaticamente propriedades JavaScript que sustentam todos os seus atributos expostos. E qualquer pessoa que crie manualmente um elemento personalizado vanilla também é incentivada a seguir essa prática . Gostaríamos de ver a comunicação em tempo de execução com elementos personalizados no React usando propriedades JavaScript por padrão.

Este documento descreve algumas propostas de como o React pode ser atualizado para que isso aconteça.

Propostas

Opção 1: definir apenas propriedades

Em vez de tentar decidir se uma propriedade ou atributo deve ser definido, o React sempre pode definir propriedades em elementos personalizados. O React NÃO verifica se a propriedade existe no elemento de antemão.

Exemplo:

<x-foo bar={baz}>

O código acima resultaria em React configurando a propriedade .bar do elemento x-foo igual ao valor de baz .

Para nomes de propriedades camelCased, o React poderia usar o mesmo estilo que usa hoje para propriedades como tabIndex .

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

Prós

Fácil de entender / implementar

Este modelo é simples, explícito e se encaixa na "API centrada em JavaScript para o DOM" do React.

Qualquer elemento criado com bibliotecas como Polymer ou Skate gerará automaticamente propriedades para apoiar seus atributos expostos. Todos esses elementos devem "simplesmente funcionar" com a abordagem acima. Os desenvolvedores que criam componentes básicos à mão são incentivados a fornecer atributos com propriedades que refletem o quão modernos (ou seja, não excêntricos como <input> ) elementos HTML5 ( <video> , <audio> , etc.) foram implementados.

Evita conflito com atributos globais futuros

Quando o React define um atributo em um elemento personalizado, sempre há o risco de que uma versão futura do HTML envie um atributo com nome semelhante e quebre as coisas. Essa preocupação foi discutida com os autores das especificações, mas não há uma solução clara para o problema. Evitar totalmente os atributos (exceto quando um desenvolvedor define explicitamente um usando ref ) pode contornar esse problema até que os navegadores encontrem uma solução melhor.

Aproveita a "atualização" do elemento personalizado

Os elementos personalizados podem ser atualizados lentamente na página e alguns padrões PRPL contam com essa técnica. Durante o processo de atualização, um elemento personalizado pode acessar as propriedades passadas a ele pelo React - mesmo se essas propriedades foram definidas antes do carregamento da definição - e usá-las para renderizar o estado inicial.

Elementos personalizados tratados como qualquer outro componente React

Quando os componentes do React passam dados uns para os outros, eles já usam propriedades. Isso faria com que os elementos personalizados se comportassem da mesma maneira.

Contras

Possivelmente uma mudança significativa

Se um desenvolvedor tiver criado elementos personalizados vanilla que têm apenas uma API de atributos, ele precisará atualizar seu código ou seu aplicativo será interrompido. A correção seria usar um ref para definir o atributo (explicado abaixo).

Precisa de ref para definir o atributo

Ao alterar o comportamento para que as propriedades sejam preferidas, significa que os desenvolvedores precisarão usar um ref para definir explicitamente um atributo em um elemento personalizado.

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

Isso é apenas uma reversão do comportamento atual em que os desenvolvedores precisam de ref para definir uma propriedade. Uma vez que os desenvolvedores raramente precisam definir atributos em elementos personalizados, isso parece uma troca razoável.

Não está claro como a renderização do lado do servidor funcionaria

Não está claro como esse modelo seria mapeado para elementos personalizados de renderização do lado do servidor. O React poderia supor que as propriedades mapeiam para atributos com nomes semelhantes e tentam defini-los no servidor, mas isso está longe de ser à prova de balas e possivelmente exigiria uma heurística para coisas como propriedades camelCased -> atributos com traço.

Opção 2: Propriedades, se disponíveis

No tempo de execução, o React pode tentar detectar se uma propriedade está presente em um elemento personalizado. Se a propriedade estiver presente, o React a usará, caso contrário, retornará para a configuração de um atributo. Este é o modelo que Preact usa para lidar com elementos personalizados.

Implementação do pseudocódigo:

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

Passos possíveis:

  • Se um elemento tiver uma propriedade definida, o React a usará.

  • Se um elemento tiver uma propriedade indefinida e o React estiver tentando passar dados primitivos (string / número / booleano), ele usará um atributo.

    • Alternativa: Avisar e não definir.
  • Se um elemento tiver uma propriedade indefinida e o React estiver tentando passar um objeto / array para ele, ele o definirá como uma propriedade. Isso ocorre porque some-attr = "[Object Object]” não é útil.

    • Alternativa: Avisar e não definir.
  • Se o elemento estiver sendo renderizado no servidor e o React estiver tentando passar uma string / número / booleano, ele usará um atributo.

  • Se o elemento estiver sendo renderizado no servidor e o React estiver tentando passar um objeto / array para ele, ele não fará nada.

Prós

Mudança ininterrupta

É possível criar um elemento personalizado que usa apenas atributos como interface. Este estilo de autoria NÃO é encorajado, mas pode acontecer independentemente. Se um autor de elemento personalizado está contando com esse comportamento, essa alteração não seria interrompida para ele.

Contras

Os desenvolvedores precisam entender a heurística

Os desenvolvedores podem ficar confusos quando o React define um atributo em vez de uma propriedade, dependendo de como escolheram carregar seu elemento.

Retornar aos atributos pode entrar em conflito com futuros globais

Sebastian levantou a preocupação de que usar in para verificar a existência de uma propriedade em um elemento personalizado poderia detectar acidentalmente uma propriedade na superclasse (HTMLElement).

Existem também outros conflitos potenciais com atributos globais discutidos anteriormente neste documento.

Opção 3: diferencie as propriedades com um sigilo

O React pode continuar definindo atributos em elementos personalizados, mas fornece um sigilo que os desenvolvedores podem usar para definir propriedades explicitamente. Isso é semelhante à abordagem usada por Glimmer.js .

Exemplo de brilho:

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

No exemplo acima, o @ sigil indica que src e hiResSrc devem passar dados para o elemento personalizado usando propriedades, e width deve ser serializado para uma string de atributo.

Como os componentes do React já passam dados uns para os outros usando propriedades, não haveria necessidade de usar o sigilo (embora funcionasse se o fizessem, seria apenas redundante). Em vez disso, ele seria usado principalmente como uma instrução explícita para passar dados para um elemento personalizado usando propriedades JavaScript.

h / t para @developit de Preact por sugerir esta abordagem :)

Prós

Mudança ininterrupta que os desenvolvedores podem optar por aderir

Todos os aplicativos de elemento personalizado React + pré-existentes continuariam a funcionar exatamente como antes. Os desenvolvedores podem escolher se desejam atualizar seu código para usar o novo estilo de sigilo.

Semelhante a como outras bibliotecas lidam com atributos / propriedades

Semelhante ao Glimmer, tanto Angular quanto Vue usam modificadores para diferenciar entre atributos e propriedades.

Vue exemplo:

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

Exemplo angular:

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

O sistema é explícito

Os desenvolvedores podem dizer ao React exatamente o que desejam, em vez de confiar em uma heurística como a abordagem de propriedades se disponíveis .

Contras

É uma nova sintaxe

Os desenvolvedores precisam ser ensinados como usá-lo e deve ser testado exaustivamente para garantir que é compatível com versões anteriores.

Não está claro como a renderização do lado do servidor funcionaria

O sigilo deve passar a usar um atributo com nome semelhante?

Opção 4: adicionar um objeto de atributos

O React pode adicionar sintaxe adicional que permite aos autores passar explicitamente os dados como atributos. Se os desenvolvedores não usarem este objeto de atributos, seus dados serão transmitidos usando propriedades JavaScript.

Exemplo:

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

Essa ideia foi proposta originalmente por @treshugart , autor de Skate.js, e é implementada na biblioteca val .

Prós

O sistema é explícito

Os desenvolvedores podem dizer ao React exatamente o que desejam, em vez de confiar em uma heurística como a abordagem de propriedades se disponíveis .

Estender a sintaxe também pode resolver problemas com o tratamento de eventos

Nota: Isso está fora do escopo deste documento, mas talvez valha a pena mencionar :)

O problema nº 7901 solicita que o React ignore seu sistema de eventos sintéticos quando manipuladores de eventos declarativos são adicionados a elementos personalizados. Como os nomes de eventos de elementos personalizados são strings arbitrárias, isso significa que podem ser capitalizados de qualquer maneira. Para contornar o sistema de eventos sintéticos hoje, também significará a necessidade de criar uma heurística para mapear nomes de eventos de JSX para addEventListener .

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

No entanto, se a sintaxe for estendida para permitir atributos, ela também poderá ser estendida para permitir eventos:

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

Neste modelo, o nome da variável é usado como o nome do evento. Nenhuma heurística é necessária.

Contras

É uma nova sintaxe

Os desenvolvedores precisam ser ensinados como usá-lo e deve ser testado exaustivamente para garantir que é compatível com versões anteriores.

Pode ser uma mudança significativa

Se algum componente já depender de propriedades chamadas attrs ou events , ele poderá quebrá-los.

Pode ser uma mudança maior do que qualquer uma das propostas anteriores

Para o React 17, pode ser mais fácil fazer uma mudança incremental (como uma das propostas anteriores) e posicionar essa proposta como algo a ser levado em consideração para um refatorador posterior maior.

Opção 5: uma API para consumir elementos personalizados

Esta proposta foi oferecida por @sophiebits e @gaearon da equipe React

O React pode criar uma nova API para consumir elementos personalizados que mapeiam o comportamento do elemento com um objeto de configuração.

Exemplo de pseudocódigo:

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

O código acima retorna um componente proxy, XFoo que sabe como passar dados para um elemento personalizado, dependendo da configuração fornecida. Você usaria esse componente proxy em seu aplicativo em vez de usar o elemento personalizado diretamente.

Exemplo de uso:

<XFoo someRichDataProp={...} />

Prós

O sistema é explícito

Os desenvolvedores podem dizer ao React o comportamento exato que desejam.

Mudança ininterrupta

Os desenvolvedores podem optar por usar o objeto ou continuar usando o sistema atual.

Idiomático para reagir

Essa mudança não requer uma nova sintaxe JSX e se parece mais com outras APIs no React. Por exemplo, PropTypes (embora esteja sendo movido para seu próprio pacote) tem uma abordagem um tanto semelhante.

Contras

Pode ser muito trabalhoso para um componente complexo

O elemento de entrada de

Pode aumentar o tamanho do pacote

Relacionado ao ponto acima, cada classe de elemento customizado agora incorre no custo de sua definição + seu tamanho de objeto de configuração.

Observação: não tenho 100% de certeza se isso é verdade.

A configuração precisa acompanhar o ritmo do componente

Cada vez que o componente faz uma revisão de versão secundária que adiciona uma nova propriedade, a configuração também precisará ser atualizada. Isso não é difícil, mas adiciona manutenção. Talvez, se os configs forem gerados a partir da fonte, isso seja menos trabalhoso, mas isso pode significar a necessidade de criar uma nova ferramenta para gerar configs para cada biblioteca de componentes da web.

cc @sebmarkbage @gaearon @developit @treshugart @justinfagnani

DOM Discussion

Comentários muito úteis

Ei pessoal, enquanto esperamos, criei um shim para envolver seu componente da web em 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>
  </>
)

Espero que isso seja útil

(Esta é minha primeira incursão em OSS, e uma das primeiras fontes abertas de algo fora do meu escritório. Críticas construtivas são mais do que bem-vindas 😄)

Todos 129 comentários

Peço desculpas pela longa leitura, mas queria ter certeza de que estava explorando exaustivamente cada opção. Não quero enviesar muito as coisas com minha própria opinião, mas se eu estivesse em posição de escolher, acho que escolheria a opção 3.

A opção 3 é compatível com versões anteriores, declarativa e explícita. Não há necessidade de manter uma heurística de fallback, e outras bibliotecas já fornecem sigilos / modificadores semelhantes.

Peço desculpas pela longa leitura, mas queria ter certeza de que estava explorando exaustivamente cada opção. Não quero enviesar muito as coisas com minha própria opinião, mas se eu estivesse em posição de escolher, acho que escolheria a opção 3.
A opção 3 é compatível com versões anteriores, declarativa e explícita. Não há necessidade de manter uma heurística de fallback, e outras bibliotecas já fornecem sigilos / modificadores semelhantes.

Estou entre a opção 2 e a opção 3, acho que o React lidou muito bem com as mudanças de comportamento e de API no passado. Introduzir avisos e links para documentos pode servir bem para ajudar os desenvolvedores a entender o que está acontecendo nos bastidores.

A opção 3 parece atraente devido à sua natureza declarativa, enquanto lêem o código JSX, os novos desenvolvedores saberão imediatamente o que o React fará ao renderizar o elemento.

Comentários sobre a opção 2

Os desenvolvedores podem ficar confusos quando o React define um atributo em vez de uma propriedade, dependendo de como escolheram carregar seu elemento.

Os consumidores de um elemento personalizado precisam entender essa distinção? Ou isso é importante apenas para o autor do elemento personalizado? Parece que o autor do elemento precisará manipular atributos para qualquer coisa usada em HTML (uma vez que essa é a única maneira de os dados serem transmitidos do uso de HTML) e propriedades se quiserem oferecer suporte a valores complexos ou obter / definir propriedades do DOM. É até possível que um autor possa ter algo inicialmente implementado como um atributo e depois adicionar uma propriedade com o mesmo nome para suportar tipos de dados mais flexíveis e ainda voltar a propriedade com um valor armazenado nos atributos.

Colisões de nomenclatura com atributos e propriedades HTMLElement futuros parecem uma fraqueza nos padrões de componentes da Web em geral, pois isso pode levar a erros, independentemente da abordagem de ligação.

Se um elemento tiver uma propriedade indefinida e o React estiver tentando passar um objeto / array para ele, ele o definirá como uma propriedade. Isso ocorre porque some-attr = "[Object Object]” não é útil.

Parece confuso vincular de forma diferente com base no valor. Se o autor do elemento não especificou um getter / setter de propriedade para manipular o valor, definir a propriedade faria com que o elemento se comportasse como se o valor nunca tivesse sido especificado, o que pode ser mais difícil de depurar.

Comentários sobre a opção 3

Outra desvantagem potencial com a opção 3 é que ela exige que o consumidor do elemento personalizado saiba se o elemento implementou algo como uma propriedade ou como um atributo. Se você estiver usando uma combinação de componentes React e elementos personalizados, pode ser confuso definir os props do React usando uma sintaxe e as propriedades do elemento personalizado usando uma sintaxe diferente.

Os consumidores de um elemento personalizado precisam entender essa distinção? Ou isso é importante apenas para o autor do elemento personalizado?

Duvido que seja realmente um grande problema porque, como você apontou, o autor do elemento deve definir um atributo e uma propriedade para o valor subjacente e aceitar os dados de ambos. Eu também acrescentaria que eles devem manter o atributo e a propriedade em sincronia (portanto, definir um define o outro).

Colisões de nomenclatura com atributos e propriedades HTMLElement futuros parecem uma fraqueza nos padrões de componentes da Web em geral, pois isso pode levar a erros, independentemente da abordagem de ligação.

Eu concordo, mas não tenho certeza se isso é algo que o React precisa tentar resolver em sua biblioteca. Parece um problema que precisa ser resolvido como parte das especificações dos elementos personalizados. Posso ver se podemos discutir isso como parte da próxima reunião de padrões TPAC.

Devo acrescentar, para propriedades isso não é _tão_ ruim porque a propriedade definida pelo elemento irá sombrear a propriedade futura adicionada ao HTMLElement. Portanto, se você estivesse passando dados para um elemento personalizado como uma propriedade js, ele continuaria a funcionar. O principal problema parece ser em torno dos atributos, uma vez que são globais.

Parece confuso vincular de forma diferente com base no valor. Se o autor do elemento não especificou um getter / setter de propriedade para manipular o valor, definir a propriedade faria com que o elemento se comportasse como se o valor nunca tivesse sido especificado, o que pode ser mais difícil de depurar.

No caso em que um elemento personalizado é carregado lentamente e "atualizado", ele inicialmente terá propriedades indefinidas. Isso aborda esse caso de uso, garantindo que esses elementos ainda recebam seus dados e possam usá-los após a atualização.

É verdade que se o autor não definir um getter / setter para um valor, isso não seria muito útil. Mas também não é útil ter um my-attr=[object Object] . E como você não sabe se a propriedade é realmente indefinida ou se a definição está apenas sendo carregada lentamente, parece mais seguro definir a propriedade.

Outra desvantagem potencial com a opção 3 é que ela exige que o consumidor do elemento personalizado saiba se o elemento implementou algo como uma propriedade ou como um atributo.

Acho que você está essencialmente no mesmo barco hoje porque não há nada que force um autor de elemento personalizado a definir um atributo em vez de uma propriedade. Portanto, eu poderia ter um elemento com uma API somente de propriedades que não receberia nenhum dado do sistema atual do React e precisaria saber como usar ref para definir diretamente as propriedades js.

Como os elementos personalizados são considerados primitivos, não há nada que imponha a criação de atributos e propriedades correspondentes. Mas estamos tentando muito encorajar isso como uma prática recomendada, e todas as bibliotecas que conheço hoje criam propriedades de apoio para seus atributos.

[editar]

Como você afirmou em seu ponto anterior:

Parece que o autor do elemento precisará manipular atributos para qualquer coisa usada em HTML (uma vez que essa é a única maneira de os dados serem transmitidos do uso de HTML) e propriedades se quiserem oferecer suporte a valores complexos ou obter / definir propriedades do DOM.

Como você nunca sabe como um usuário tentará passar dados para o seu elemento, você acaba precisando ter correspondência de atributo-propriedade de qualquer maneira. Eu imagino que se a opção 3 fosse enviada, a maioria das pessoas simplesmente vincularia tudo usando o sigilo @ porque seria o mais fácil. É assim que trabalho com elementos personalizados no Vue hoje, já que eles expõem um modificador .prop .

requer que o consumidor do elemento personalizado saiba se o elemento implementou algo como uma propriedade ou como um atributo

Isso não é algo que o React deva se preocupar, como Rob disse na minha opinião, é responsabilidade do autor do elemento customizado informar ao usuário como o elemento funciona.

E é realmente a maneira que precisamos fazer hoje, por exemplo, pense no elemento <video> , digamos que você precise silenciá-lo ou alterar a hora atual dentro de um componente.

muted funciona como um atributo booleano

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

No momento, você precisa criar um ref apontando para o elemento de vídeo e alterar a propriedade.

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

Em seguida, crie um manipulador de eventos, um método de instância e defina manualmente a propriedade para o elemento DOM.

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

Se você pensar sobre isso, meio que quebra o modelo declarativo que o próprio React impõe com sua API e camada abstrata JSX, uma vez que currentTime é claramente um estado no componente wrapper, com associação de propriedade ainda precisaríamos do manipulador de eventos, mas o O modelo de abstração JSX seria mais declarativo e refs não seriam necessários apenas para isso:

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

Meu ponto é que se você está contando com elementos nativos ou customizados, você ainda precisa conhecê-los com base na documentação, a diferença que no segundo caso deve vir do autor do elemento customizado.

@cjorasch meus dois centavos :)

Se estivéssemos projetando isso do zero, sem precisar considerar a compatibilidade com versões anteriores, acho que a opção 1 seria a mais idiomática de acordo com a "API centrada em JavaScript para o DOM" do React.

Com relação à renderização do lado do servidor, esse problema poderia ser resolvido fornecendo uma API para o código do aplicativo para informar o React sobre como mapear propriedades de elementos personalizados para atributos? Semelhante aos mapas que o React já mantém para atributos definidos pela plataforma? Essa API só precisaria ser chamada uma vez por nome de elemento personalizado (não para cada instância dele), e apenas para propriedades que não seguem uma correspondência 1: 1 direta com seu atributo, o que deve ser relativamente raro.

Porém, se estamos preocupados com o fato de que essa alteração é muito significativa, então acho que a opção 3 também é muito atraente. Se o sigilo significar uma propriedade, eu sugeriria ".", Uma vez que esse já é o acessador de propriedade do JavaScript. No entanto, acho lamentável que cada instância de onde um elemento personalizado é usado em um aplicativo seja responsável por saber quando usar um atributo e quando usar uma propriedade. O que eu prefiro sobre a opção 1 é que, mesmo se uma propriedade para atribuir o mapa for necessária, esse código de mapeamento pode ser isolado de todos os usos do JSX.

No caso em que um elemento personalizado é carregado lentamente e "atualizado", ele inicialmente terá propriedades indefinidas. Isso aborda esse caso de uso, garantindo que esses elementos ainda recebam seus dados e possam usá-los após a atualização.

Talvez eu não entenda o processo de atualização. Os elementos normalmente teriam propriedades definidas como getters / setters no protótipo de classe. Verificar propName in element retornaria verdadeiro devido à existência do getter / setter, mesmo se o valor da propriedade ainda fosse indefinido. Durante a atualização, os valores da propriedade são definidos em alguma instância temporária e, posteriormente, são copiados para a instância real quando o carregamento lento é concluído?

A atualização é o processo pelo qual o elemento personalizado recebe sua classe. Antes disso, não é uma instância dessa classe, portanto, os getters / setters de propriedade não estão disponíveis.

@jeremenichelli

mute funciona como um atributo booleano

acabei de verificar e também tem uma propriedade correspondente, embora não pareça estar documentada no MDN: P

No momento, você precisa criar um ref apontando para o elemento de vídeo e alterar a propriedade.

Sim, ocasionalmente você encontrará APIs somente de propriedades em elementos HTML modernos. currentTime atualiza em alta frequência, então não faria sentido refleti-lo em um atributo HTML.

Meu ponto é que se você está contando com elementos nativos ou personalizados, você ainda precisa saber como contorná-los com base na documentação

Sim, infelizmente não existe uma regra de atributos / propriedades de tamanho único. Mas acho que, de um modo geral, você pode confiar fortemente nas propriedades e fornecer sintaxe para que os desenvolvedores possam usar atributos em casos especiais.

@robdodson sim , eu sabia sobre a propriedade mudo também 😄 Eu apenas usei esses dois para provar que já _na selva_ não existe uma regra que sirva para todos, como você mencionou.

Teremos que contar com a documentação dos elementos nativos e personalizados, então é algo que eu não me importaria nesta decisão.

Enquanto escrevia o último trecho de código, eu meio que gostei da vinculação de propriedades 💟

@effulgentsia

No entanto, acho lamentável que cada instância de onde um elemento personalizado é usado em um aplicativo seja responsável por saber quando usar um atributo e quando usar uma propriedade.

Acho que já é o caso hoje. Uma vez que as principais bibliotecas de elementos personalizados (polímero, skate, possivelmente outros?) Criam automaticamente propriedades de apoio para todos os atributos expostos, os desenvolvedores podem apenas usar o sigilo para cada propriedade em um elemento personalizado. Provavelmente seria uma ocorrência rara para eles precisarem mudar para o uso de um atributo.

@cjorasch

RE: atualização. Como @effulgentsia mencionou, é possível ter um elemento personalizado na página, mas carregar sua definição posteriormente. <x-foo> será inicialmente uma instância de HTMLElement e quando carrego sua definição posteriormente, ele "atualiza" e se torna uma instância da classe XFoo . Nesse ponto, todos os retornos de chamada do ciclo de vida são executados. Usamos essa técnica no projeto do Polymer Starter Kit. Mais ou menos assim:

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

No exemplo acima, não carregaremos a definição de my-view2 até que o roteador mude para ela.

É inteiramente possível definir uma propriedade no elemento antes de ter sido atualizado e, uma vez que a definição é carregada, o elemento pode obter esses dados durante um de seus retornos de chamada do ciclo de vida.

os desenvolvedores podem apenas usar o sigilo para cada propriedade em um elemento personalizado

Se os desenvolvedores começassem a fazer isso, como isso diferenciaria o uso de uma propriedade porque você "pode" de usar uma propriedade porque "deve"? E essa não é uma diferenciação necessária para a renderização do lado do servidor?

Se os desenvolvedores começassem a fazer isso, como isso diferenciaria o uso de uma propriedade porque você "pode" de usar uma propriedade porque "deve"?

Desculpe, talvez eu tenha dito errado. Eu quis dizer que os desenvolvedores provavelmente usariam o sigilo porque daria o resultado mais consistente. Você pode usá-lo para passar dados primitivos ou dados ricos como objetos e matrizes e sempre funcionará. Acho que trabalhar com propriedades em tempo de execução geralmente é preferível a trabalhar com atributos, pois os atributos tendem a ser usados ​​mais para a configuração inicial.

E essa não é uma diferenciação necessária para a renderização do lado do servidor?

Pode ser o caso de que no servidor o sigilo retorne para a configuração de um atributo.

Pode ser o caso de que no servidor o sigilo retorne para a configuração de um atributo.

Eu não acho que funcionaria se a razão para o sigilo for que é uma propriedade que não existe como um atributo, como currentTime do vídeo.

diferencie usando uma propriedade porque você "pode" de usar uma propriedade porque você "deve"

Acho que essa diferenciação é importante, porque há razões totalmente diferentes para escolher usar um atributo ou propriedade como uma otimização (por exemplo, SSR preferindo atributos vs. renderização do lado do cliente preferindo propriedades) vs. algo que existe como apenas um atributo ou apenas uma propriedade.

Com relação à renderização do lado do servidor, esse problema poderia ser resolvido fornecendo uma API para o código do aplicativo para informar o React sobre como mapear propriedades de elementos personalizados para atributos?

Para ser mais específico, estou sugerindo algo assim:

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

Exemplos:

// '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)

Para algo que só pode ser uma propriedade (em que htmlAttributeName é nulo), o SSR pularia a renderização e, em seguida, hidrataria no cliente.

Para algo que só pode ser um atributo (onde domPropertyName é nulo), o React invocaria setAttribute() como atualmente na v16.

Para algo que pode ser ambos, o React pode escolher a estratégia mais adequada. Talvez isso signifique sempre definir como uma propriedade do lado do cliente, mas como um atributo do lado do servidor. Talvez isso signifique definir como um atributo ao criar inicialmente o elemento, mas definir como uma propriedade ao fazer o patch posterior do vdom. Talvez signifique apenas definir como um atributo quando o valor for um tipo primitivo. Idealmente, o React deve ser capaz de alterar a estratégia sempre que desejar, como um detalhe de implementação interno.

Quando o React encontra um prop para o qual defineCustomElementProp() não foi chamado e que não é definido pela especificação HTML como uma propriedade ou atributo global, o React pode implementar alguma lógica padrão. Por exemplo, talvez:

  • Na versão 17, mantenha BC com v16 e defina como um atributo.
  • Na versão 18, suponha que pode ser qualquer um e siga a estratégia ideal para isso.

Mas, em qualquer caso, mantendo esta uma API separada, os objetos JSX e props são mantidos limpos e dentro de um único namespace, assim como são para componentes React e elementos HTML não personalizados.

Desculpe pelos comentários excessivos, mas pensei em outro benefício para minha proposta acima que gostaria de compartilhar:

Essas chamadas ReactDOM.defineCustomElementProp() podem ser fornecidas em um arquivo JS mantido pelo autor do elemento personalizado (no mesmo repositório onde o elemento personalizado é mantido / distribuído). Não seria necessário para elementos personalizados com uma correspondência estrita de 1: 1 de propriedade / atributo, que, de acordo com a declaração de plano de fundo desta edição, é a recomendação e o caso mais comum de qualquer maneira. Portanto, apenas os autores de elementos personalizados que não seguirem essa recomendação precisarão fornecer o arquivo de integração React. Se o autor não o fornecer (por exemplo, porque o autor do elemento personalizado não se preocupa com o React), então a comunidade de pessoas que usam esse elemento personalizado nos aplicativos React pode organizar um repositório central para abrigar esse arquivo de integração.

Eu acho que a possibilidade de tal centralização é preferível a uma solução que exige que cada usuário do elemento personalizado sempre tenha que ser explícito com um sigilo.

A opção 3 seria a minha preferida, mas é uma grande mudança significativa ... E o inverso? Os atributos têm um prefixo, não adereços?

Sigils in React, não sei como me sinto a respeito. A especificação JSX deve ser considerada universal, não excessivamente dependente ou dependente das especificações do navegador, especialmente não de irregularidades devido à compatibilidade com versões anteriores. obj[prop] = value e obj.setAttributes(props, value) comportando-se de maneira diferente é lamentável, mas olhando para a API do navegador como um todo, não é uma surpresa. @ : [] faria um detalhe de implementação vazar para a superfície e contradizer a abordagem centrada em javascript. Portanto, a menos que tenhamos uma especificação que faça o seguinte, acho que é uma má ideia: const data = <strong i="8">@myFunction</strong> // -> "[object Object]"

Se eu tiver que depender de um componente da web, ficaria feliz se a semântica fosse escondida do React e do JSX, e também me certificaria de que eles não apresentassem alterações significativas. De todas as opções, deixar ref => ... no lugar parece ser favorável para mim. ref é projetado especificamente para acessar o objeto. E pelo menos o desenvolvedor sabe exatamente o que está acontecendo, não há vazamento de sigilo, nem novos atributos que poderiam quebrar projetos existentes.

@LeeCheneler

A opção 3 seria a minha preferida, mas é uma grande mudança significativa ... E o inverso? Os atributos têm um prefixo, não adereços?

Por que seria uma mudança significativa? O comportamento atual dos atributos sendo o padrão permaneceria. O sigilo seria opt-in e os desenvolvedores o usariam para substituir os pontos em seu código onde atualmente usam um ref para passar dados para um elemento personalizado como uma propriedade JS.

@drcmda

nem novos atributos que poderiam quebrar projetos existentes.

Você pode esclarecer o que você quis dizer com isso?

Para sua informação, para quem está seguindo a discussão, atualizei o RFC com uma quinta opção sugerida por membros da equipe React.

@robdodson

Eu estava me referindo a isso:

Opção 4: adicionar um objeto de atributos
Contras
Pode ser uma mudança significativa

A opção 5 parece mais segura para nós. Ele nos permite adicionar o recurso sem ter que tomar uma decisão sobre a API “implícita” agora, uma vez que o ecossistema ainda está na fase de “descobrir isso”. Sempre podemos revisitá-lo em alguns anos.

O elemento de entrada de papel do Polymer tem 37 propriedades, portanto, produziria uma configuração muito grande. Se os desenvolvedores estiverem usando muitos elementos personalizados em seus aplicativos, isso pode ser igual a muitas configurações que eles precisam escrever.

Minha impressão é que os usuários do elemento customizado no React irão eventualmente querer envolver alguns elementos customizados nos componentes do React de qualquer maneira para o comportamento / customizações específicas do aplicativo. É uma estratégia de migração mais agradável para este caso se tudo já for um componente React, por exemplo

import XButton from './XButton';

e isso é gerado por

export default ReactDOM.createCustomElementType(...)

Isso permite que eles substituam um componente React por um componente personalizado que usa (ou mesmo não usa) elementos personalizados a qualquer momento.

Portanto, se as pessoas vão criar componentes React em pontos de interoperabilidade, podemos também fornecer um auxiliar poderoso para fazer isso. Também é provável que as pessoas compartilhem essas configurações para os elementos personalizados que usam.

E, eventualmente, se virmos o ecossistema se estabilizar, podemos adotar uma abordagem sem configuração.

Acho que a próxima etapa aqui seria escrever uma proposta detalhada de como a configuração deve se parecer para satisfazer todos os casos de uso comuns. Deve ser atraente o suficiente para os usuários do elemento personalizado + React, já que se não responder a casos de uso comuns (como manipulação de eventos), vamos acabar no limbo, onde o recurso não fornece benefícios suficientes para compensar o detalhamento .

Com base no meu comentário anterior , que tal:

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

A lógica seria, para cada suporte React em uma instância XFoo:

  1. Se eventName para esse prop não for nulo, registre-o como um manipulador de eventos que invoca o valor do prop (considerado uma função).
  2. Do contrário, se a renderização do lado do cliente e propertyName não for nula, defina a propriedade do elemento para o valor prop.
  3. Caso contrário, se attributeName não for nulo, defina o atributo do elemento para o valor da propriedade stringified. Se attributeSerializer não for nulo, use-o para transformar o valor da proposta em sequência. Caso contrário, apenas faça '' + propValue .

O elemento de entrada de papel do Polymer tem 37 propriedades, portanto, produziria uma configuração muito grande.

Eu gostaria de sugerir que a configuração só seja necessária para adereços atípicos. Para qualquer suporte na instância XFoo que não foi incluído na configuração, o padrão é:

  • se o valor for uma função:
eventName: the prop name,
  • outro:
propertyName: the prop name,
attributeName: camelCaseToDashCase(the prop name),

Alternativamente, talvez faça sentido manter os eventos em um namespace separado, nesse caso, remova tudo o que está relacionado com eventName do último comentário e, em vez disso, deixe os eventos serem registrados como:

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

@gaearon @effulgentsia o que vocês acham de uma combinação da opção 1 e da opção 5?

A opção 1 tornaria mais fácil para o usuário casual de um elemento personalizado passar dados ricos. Estou imaginando o cenário em que estou construindo um aplicativo e só quero usar alguns elementos personalizados. Eu já sei como eles funcionam e não estou tão interessado a ponto de querer escrever uma configuração para eles.

A opção 5 seria para pessoas que desejam usar algo como entrada de papel em todo o aplicativo e realmente gostariam de expor toda a API para todos em sua equipe.

Para SSR da opção 1, a heurística pode ser sempre usar um atributo se renderizar no servidor. Uma propriedade camelCase é convertida em um atributo de caixa do travessão. Esse parece ser um padrão bastante comum em bibliotecas de componentes da web.

Gosto muito da ideia de uma combinação opção1 + opção5. O que significa que para a maioria dos elementos personalizados:

<x-foo prop1={propValue1}>

funcionaria como esperado: prop1 definido como uma propriedade do lado do cliente e como um atributo (com traço) do lado do servidor.

E as pessoas podem mudar para a opção 5 para qualquer coisa que não seja adequada para elas.

Seria uma mudança significativa em relação à forma como o React 16 funciona. Para qualquer pessoa que experimente essa quebra (por exemplo, eles estavam usando um elemento personalizado com atributos que não são apoiados por propriedades), eles poderiam mudar para a opção 5, mas ainda é uma quebra. Deixo para a equipe React decidir se isso é aceitável.

Ah, isso é o que eu ganho por ler isso rapidamente no trem @robdodson 🤦‍♂️ ... Não sou muito fã da opção 3 agora 🤔 Eu li isso como um all-in em adereços sendo prefixados, daí minha hesitação.

A opção 5 parece razoável e direta.

Eu gosto de para onde @effulgentsia está indo. Existe uma razão para que não pudesse ser:

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

Ou o suporte a vários tipos em um único acessório é valioso?

Eu ficaria hesitante com esse fluxo embora @effulgentsia :

se o valor for uma função:
eventName: o nome do prop,
outro:
propertyName: o nome do prop,
attributeName: camelCaseToDashCase (o nome da propriedade),

Não acho que gostaria que uma função prop fosse padronizada para um evento, e atribuir tanto propertyName quanto attributeName é sensato? Quando você deseja que ambos tenham suporte para simular a pergunta acima? 🙂

@LeeCheneler :

Citando os prós da Opção 1 do resumo do problema:

Qualquer elemento criado com bibliotecas como Polymer ou Skate gerará automaticamente propriedades para apoiar seus atributos expostos. Todos esses elementos devem "simplesmente funcionar" com a abordagem acima. Os desenvolvedores que criam componentes básicos à mão são incentivados a fornecer atributos com propriedades que refletem o quão modernos (ou seja, não excêntricos como <input> ) elementos HTML5 ( <video> , <audio> , etc.) foram implementados.

Portanto, essa é a razão pela qual atribuir tanto propertyName quanto attributeName é sensato: porque reflete o que é realmente o caso para elementos que seguem as melhores práticas. E ao tornar o React ciente disso, permite que o React decida qual usar com base na situação: como usar propriedades para renderização do lado do cliente e atributos para renderização do lado do servidor. Para elementos personalizados que não seguem as práticas recomendadas e têm alguns atributos sem propriedades correspondentes e / ou algumas propriedades sem atributos correspondentes, o React precisa estar ciente disso, para que as propriedades sem atributos não sejam renderizadas durante o SSR e a propriedade -less-attribute pode ser definido com setAttribute () durante a renderização do lado do cliente.

Com sua proposta, isso poderia ser feito por sinalizadores de combinação de bits, como:

propName1: T.Property | T.Attribute,

No entanto, isso não forneceria uma maneira de expressar que o nome do atributo é diferente do nome da propriedade (por exemplo, camelCase para dash-case). Nem forneceria uma maneira de expressar como serializar um objeto rico para um atributo durante o SSR (o comportamento atual de "[Object Object]" não é útil).

Não acho que gostaria que um suporte de função fosse padronizado para um evento

Sim, acho que também concordo com isso, daí o comentário de acompanhamento . Obrigado por validar minha hesitação com isso!

Aqui está uma ideia para uma versão menos prolixa da minha sugestão anterior :

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

E de acordo com o comentário de código acima, eu recomendo fortemente não exigir que todas as propriedades refletidas sejam definidas, mas sim padronizar qualquer coisa que não seja definida automaticamente como sendo uma propriedade refletida cujo nome de atributo é a versão com traço.

Faz sentido @effulgentsia 👍

Eu gosto do seu segundo exemplo, mas ele não está aberto a uma explosão combinatória se mais tipos forem adicionados, eventos ala + o que quer que faça sentido?

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

Embora eu suponha que você não gostaria de misturar um evento com um adereço ou atributo de qualquer maneira 🤔 Atributo e prop são provavelmente as únicas coisas que você gostaria de imitar.

Acho que há uma oportunidade aqui para a comunidade React e o Web Component se alinharem com as melhores práticas. Reagir com uma opinião aqui irá percorrer um longo caminho para que os autores de elementos personalizados sejam guiados na direção certa devido à sua adoção generalizada e ao peso que suas opiniões carregam.

Embora eu tenha sido o autor da implementação da opção 4, sempre sou pego por ter que separar atributos e eventos de propriedades. Idealmente, eu preferiria a opção 1. Praticamente, acho que preferiria a opção 2 com uma saída de emergência.

A opção 1 é ideal, mas existem muitos tipos de atributos que não têm propriedades correspondentes (ária / dados), então ela exigiria heurísticas extras em torno deles e se houver casos extremos onde os elementos podem ter um atributo que deveria ter um propriedade, mas não a implemente por qualquer motivo. Acho que essa é uma opção que deve ser considerada com cuidado, mas pode ser viável a longo prazo.

A opção 2 é preferível porque é uma alteração ininterrupta. Ele funcionará para todas as situações em que a definição do elemento personalizado é registrada antes de uma instância ser criada (ou carregada antes que as propriedades sejam definidas). A arte anterior para esta opção é Preact (cc @developit) e tem funcionado bem até agora. Seguir esse caminho fornece uma implementação razoavelmente robusta e ininterrupta que foi comprovada por uma variante React de sucesso e funciona na maioria das situações. No mínimo, isso dá ao React uma solução de curto prazo, enquanto soluções melhores são avaliadas para o longo prazo.

Para a situação (situação s ?) Em que não funciona - carregamento adiado de definições de elemento personalizado e quaisquer outras que não cobrimos - uma saída de emergência como o que o DOM incremental fez pode ser implementada. Isso é semelhante ao que @effulgentsia está propondo, mas seria melhor escalar para x número de elementos personalizados. Se os consumidores quiserem fazer isso por elemento personalizado, eles ainda podem, porque é apenas uma função. Isso permite que o React tenha uma opinião, escape e satisfaça todos os casos de uso, transferindo a responsabilidade para o consumidor por todos os casos extremos. Isso também é algo que já discuti anteriormente com @developit sobre a implementação no Preact. O alinhamento entre Preact / React aqui seria uma grande vitória.

Sobre a preocupação com os atributos HTML futuros, isso é algo que não podemos resolver, então não acho que devamos nos preocupar com isso aqui.

Essas chamadas ReactDOM.defineCustomElementProp() podem ser fornecidas em um arquivo JS mantido pelo autor do elemento personalizado

Isso vincularia a implementação do elemento personalizado à implementação específica da biblioteca (neste caso, React). Em minha opinião, isso é um fardo muito alto para os autores de elementos personalizados. Além disso, para os autores que não usam React, convencê-los a enviar a definição entra em discussões políticas que ninguém quer.

Definir a invocação de tal API pelo usuário de um elemento personalizado com apenas as propriedades / atributos que o usuário realmente usa é menos código para manter e mais expressivo.

Mesmo que o autor de uma biblioteca não use React, estou argumentando que é uma UX melhor para o cliente desses componentes da web definir a configuração da propriedade uma vez e depois usá-la.

A primeira vez que você usa um componente da web que precisa de uma propriedade, é tão difícil quanto adicionar um sigilo (mais detalhado, mas também mais explícito), mas então é mais fácil para usos subsequentes porque você não precisa pensar nisso para cada callite desse componente. Em ambos os casos, você precisa entender a diferença de propriedades / atributos ao adotar um novo componente da web, mas com a configuração única compartilhada, você não precisa que todos os usuários desse componente no mesmo aplicativo pensem nisso.

Poderíamos permitir o remapeamento de nomes de propriedades nessa configuração para permitir a criação de mais nomes idiomáticos do React lá. O caminho de atualização para um wrapper mais sofisticado, se algum dia for necessário, também é mais suave, pois você pode simplesmente substituir o XFoo por um componente React personalizado que executa qualquer lógica de tradução sofisticada de que precisa.

Basicamente: se é possível que as pessoas não pensem na configuração da propriedade toda vez que usam um componente, prefiro essa abordagem. É por isso que sugeri a opção 5. Acho que é mais ergonômico do que 3 e 4, embora seja igualmente flexível.

As opções 1 e 2 não funcionam muito bem com renderização de servidor (geração de HTML), e a opção 2 também cria riscos de atualização para autores de componentes da web, onde agora adicionar uma propriedade com o mesmo nome de um atributo existente é uma alteração significativa. Se decidirmos que o SSR não é útil, a opção 1 é bastante atraente do ponto de vista do React. Não estou familiarizado se seria abrangente o suficiente na prática - os autores de componentes expõem tudo por meio de propriedades?

@treshugart sem se aprofundar muito no

Desculpe @sophiebits agora vendo sua resposta. Gostaria de responder a alguns dos pontos depois de lê-lo mais algumas vezes, mas gostaria de responder rapidamente a este:

Não estou familiarizado se seria abrangente o suficiente na prática - os autores de componentes expõem tudo por meio de propriedades?

Qualquer componente construído com Polymer ou Skate irá expor tudo por meio de propriedades. Pode haver alguns outliers raros (talvez definindo um atributo apenas para estilo ou algo assim), mas, caso contrário, acho que as coisas são sempre apoiadas por propriedades. Tenho certeza de que a maioria dos componentes da web em produção é construída usando uma dessas duas bibliotecas.

Se alguém é autoria manual de um componente da web vanilla, não há nada que o force a expor tudo por meio de propriedades, mas incentivamos que o faça em nossos documentos e exemplos de práticas recomendadas .

Para valer a pena, também não há nada que os force a usar atributos. Uma definição de elemento customizado é apenas uma classe para que o desenvolvedor possa fazer o que quiser. Mas, na prática, a maioria das pessoas prefere usar uma abstração como Polymer ou Skate para cunhar seus componentes, então eles estão obtendo uma API de propriedades gratuitamente.

Talvez então a melhor abordagem seja fazer as propriedades sempre no lado do cliente e, em seguida, oferecer suporte a um mapa de configuração no estilo da Opção 5 de nomes de propriedades primitivas para atribuir nomes a pessoas que desejam oferecer suporte a SSR em seus aplicativos.

@sophiebits : Acho que é uma ótima ideia, exceto na medida em que é uma mudança significativa para as pessoas que escreveram seus aplicativos React usando nomes de atributos. Por exemplo:

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

Mas e quanto a uma regra para "fazer propriedades" se o propname não tiver traços e "fazer atributos" se tiver?

@robdodson : você está ciente de quaisquer estruturas ou práticas de elemento personalizado lá fora, onde as pessoas teriam diferentes capitalizações de atributos da propriedade correspondente, sem conter um travessão? Ou seja, um atributo longname com uma propriedade longName ? Na verdade, esse parece ser o padrão muito mais comum com elementos HTML integrados e atributos globais (por exemplo, contenteditable => contentEditable ), mas não estou certo se agora é suficientemente desencorajado para atributos de elementos personalizados graças ao Polymer e ao Skate. Se ainda for um problema, o JSX existente com:

<x-foo longname={val} />

falharia como um conjunto de propriedades se a propriedade fosse longName .

@effulgentsia Só posso falar pelo Skate, mas só recomendamos usar o atributo API se estiver escrevendo HTML, que - para todos os efeitos - também pode ser classificado como renderização de servidor. Se você está fazendo qualquer coisa via JS, você deve definir adereços. Nossa API props faz automaticamente uma sincronização / desserialização unilateral dos atributos e deriva o nome do atributo colocando um traço no nome da propriedade (camelCase torna-se camel-case, etc). Esse comportamento como um todo é configurável, mas encorajamos a prática recomendada a ser mencionada acima.

Como afirmado por muitos, os adereços tornam o SSR difícil, e esta é uma preocupação válida. Uma vez que parece que a maioria prefere a opção 1, talvez tentemos seguir em frente com a proposta de @sophiebits de definir adereços no cliente e, ao mesmo tempo, fornecer um fallback / mapeamento? Presumo que isso significa que os atributos serão definidos no servidor?

Para fins de exemplo, veja como você poderia implementar o transporte de estado e adereços do servidor para o cliente com o Skate (e qualquer elemento personalizado que implemente renderedCallback() e props e / ou state setters. Fazer isso no nível do elemento personalizado é trivial. Se o React seguisse o caminho de definir atributos no servidor, a reidratação seria essencialmente a mesma. Os autores do componente do skate não teriam que fazer nada como nós já havia fornecido a lógica de desserialização para eles.

@robdodson Eu faria algo semelhante ao que o DOM incremental está fazendo. Isso seria algo como:

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

a opção 2 também cria riscos de atualização para autores de componentes da web, onde agora adicionar uma propriedade com o mesmo nome de um atributo existente é uma alteração significativa.

Acho que isso pode ser inevitável, dada a forma como os atributos funcionam na plataforma. Se você fizer SSR de um elemento personalizado e, portanto, precisar gravar em um atributo, também corre o risco de que a plataforma envie um atributo com nome semelhante no futuro. Como @treshugart mencionou antes , não acho que seja algo que o React (ou qualquer biblioteca) tenha poderes para resolver. Isso é algo que eu quero discutir com os autores das especificações do componente da web no TPAC para ver se podemos consertá-lo no espaço do elemento personalizado.

Não tenho certeza se isso muda sua opinião sobre a opção 2 😁, mas gostaria de mencioná-lo, já que a opção 2 tem alguns bônus agradáveis ​​e Preact parece estar provando isso na prática.

Talvez então a melhor abordagem seja fazer as propriedades sempre no lado do cliente e, em seguida, oferecer suporte a um mapa de configuração no estilo da Opção 5 de nomes de propriedades primitivas para atribuir nomes a pessoas que desejam oferecer suporte a SSR em seus aplicativos.

+1, apóio continuar a seguir nessa direção.


@effulgentsia

Acho que é uma ótima ideia, exceto na medida em que é uma mudança significativa para as pessoas que escreveram seus aplicativos React usando nomes de atributos.

A opção 2 resolveria isso :)
Caso contrário, provavelmente seria necessário haver uma heurística acoplada à opção 1 que mapeia atributos de dash-cased para propriedades camelCased.

Mas e quanto a uma regra para "fazer propriedades" se o propname não tiver traços e "fazer atributos" se tiver?

Parece que o Preact definirá o atributo se houver um traço no nome. Estou assumindo que é porque eles usam a opção 2 e long-name não passa na verificação in então retorna para um atributo ( fonte ).

Eu pessoalmente gosto desse comportamento, mas ele volta para o reino de definição de atributos e possíveis conflitos futuros, então acho que @sophiebits deve pesar.

Você está ciente de quaisquer estruturas ou práticas de elemento personalizado lá fora, onde as pessoas teriam diferentes invólucros de atributos da propriedade correspondente, sem conter um travessão?

Não que eu saiba. O traço é uma maneira fácil de a biblioteca saber onde guardar o camelo. Se você estivesse criando à mão um componente da web vanilla, poderia fazer longname/longName e apenas a opção 2 salvaria você, porque não encontraria uma propriedade longname , mas seria um substituto para a propriedade usada anteriormente Atributo longname .

Pelo que vale a pena, parece que Preact, como último recurso, chamará toLowerCase() em uma propriedade que não reconhece antes de definir o atributo. Portanto, se você estivesse usando o SSR em <x-foo longName={bar}/> , também voltaria ao atributo longname="" .


@treshugart

talvez tentemos seguir em frente com a proposta da @sophiebits de definir adereços no cliente e, ao mesmo tempo, fornecer um fallback / mapeamento? Presumo que isso significa que os atributos serão definidos no servidor?

sim e sim.

Eu faria algo semelhante ao que o DOM incremental está fazendo

É a ideia de que cada elemento (ou sua classe base) precisaria misturar isso?

A propósito, obrigado a todos por continuarem participando da discussão, eu sei que está demorando bastante ❤️

Não tenho certeza se entendi o último exemplo em https://github.com/facebook/react/issues/11347#issuecomment -339858314, mas é altamente improvável que forneçamos um gancho substituível global como este.

@gaearon Acho que Trey estava apenas mostrando a mudança líquida como um patch monkey, provavelmente seria escrito na própria implementação do ReactDOM.

Com base nos comentários recentes, o que vocês acham desta proposta?

  1. Para BC, não mude nada sobre como os elementos personalizados em minúsculas funcionam no React. Em outras palavras, JSX, como <x-foo ... /> , continuaria a funcionar da mesma maneira no React 17 e no 16. Ou, se _menores_ correções de bug forem necessárias para ele, abra um problema separado para discuti-las.

  2. Adicione uma API sem configuração para criar um componente React com melhor semântica de elemento personalizado. Por exemplo, const XFoo = ReactDOM.customElement('x-foo'); .

  3. Para o componente criado acima, use a seguinte semântica:

  4. Para qualquer prop no XFoo que seja um nome de prop do React reservado ( children , ref , qualquer outro?), Aplique a semântica usual do React a ele (não defina-o como um atributo ou propriedade no nó DOM do elemento personalizado).
  5. Para qualquer atributo ou propriedade global HTMLElement definido pela especificação HTML (incluindo data-* e aria-* ), faça com esses adereços o mesmo que o React faria com eles se estivessem em um div Elemento <XFoo data-x={} className={} contentEditable={} /> ao nó DOM deve ser idêntico a como faz para <div data-x={} className={} contentEditable={} /> (tanto para o lado do cliente quanto para SSR).
  6. Para qualquer adereço no XFoo que contenha um traço (diferente dos atributos globais permitidos acima), emita um aviso (para informar ao desenvolvedor que o XFoo é uma API centrada em propriedade, não em atributos) e não faça nada com ela (nem configurá-lo como uma propriedade nem como um atributo).
  7. Para qualquer suporte no XFoo que comece com um sublinhado ou uma letra ASCII superior, não faça nada com ele (e talvez emita um aviso?). Nenhuma das duas é uma maneira recomendada de nomear propriedades para elementos personalizados, portanto, reserve esses namespaces para semântica futura (por exemplo, consulte o item 4 abaixo).
  8. Para todos os outros adereços no XFoo, ao renderizar do lado do cliente, defina-o no nó DOM como uma propriedade. Para SSR, se o valor for primitivo, renderize-o como um atributo; se não for primitivo, pule a renderização e defina-o como uma propriedade do lado do cliente durante a hidratação. Para renderização SSR como um atributo, camelCaseToDashCase o nome.

  9. Para componentes criados por meio da API no item 2 acima, reserve um nome de propriedade para usar para ouvintes de eventos. Por exemplo, 'events' ou 'eventListeners' . Ou, se não quiser entrar em conflito com esses nomes de propriedade de elemento personalizado em potencial, '_eventListeners' ou 'EventListeners' . A implementação de XFoo criada pelo ReactDom registraria automaticamente esses ouvintes de evento no nó DOM.

  10. Para casos extremos (por exemplo, usando um elemento personalizado para o qual a implementação acima não é desejada ou suficiente), o desenvolvedor do aplicativo pode implementar seu próprio componente React para fazer qualquer coisa especial de que necessite. Ou seja, eles não precisam usar ReactDOM.customElement() ou podem estendê-lo.

  11. Para pessoas que desejam todos os comportamentos acima, mas desejam que seu JSX use nomes de elementos personalizados em minúsculas ( <x-foo /> vez de <XFoo /> , por razões semelhantes que pessoas familiarizadas com a escrita de HTML preferem <div> sobre <Div> ), eles podem fazer o monkey-patch React.createElement (). Seria um patch de macaco muito simples: apenas pegue o primeiro argumento e se ele corresponder a um nome de elemento personalizado para o qual você deseja (seja uma lista específica ou qualquer string com todas as letras minúsculas e pelo menos um traço), então invoque ReactDOM.customElement() nesse nome e encaminhar o resultado e os argumentos restantes para o React.createElement() .

@developit @gaearon pode ser qualquer um. Se um "mapeamento" fosse necessário, acho que um gancho é mais sustentável. No entanto, isso também pretendia mostrar a mudança líquida se fosse implementado no núcleo do ReactDOM, como Jason apontou.

Para BC, não mude nada sobre como os elementos personalizados em minúsculas funcionam no React. Em outras palavras, JSX gosta continuaria a funcionar da mesma maneira no React 17 e no 16. Ou, se pequenas correções de bugs forem necessárias para ele, abra um problema separado para discuti-las.

Pessoalmente, eu preferiria poder usar meu elemento <x-foo> , como está, e facilmente passar as propriedades sem precisar envolvê-lo primeiro.

Se possível, prefiro seguir a sugestão de @sohpiebits da opção 1 (propriedades do lado do cliente) e 5 (configuração para SSR). Ainda tenho esperança de que talvez as pessoas reconsiderem a opção 2 (talvez a opção 2 + 5?) Para o bônus de compatibilidade com versões anteriores.

@gaearon sua opinião muda sobre a proposta de Trey se ela fizer parte do núcleo do ReactDOM? Talvez pudéssemos dar mais detalhes ao exemplo, se isso ajudasse?

Minha principal preocupação com a Opção 5 é criar um monte de clichês para permitir a interoperabilidade, quebrando o DX, seria uma vergonha para toda a equipe principal do React e colaboradores passarem muito tempo em mudanças que não terão o impacto desejado.

Eu realmente adorei como a equipe React lidou com as últimas alterações no repo, como _PropTypes_, seria bom pensar em um plano envolvendo mais do que apenas um switch, para educar os desenvolvedores sobre as possíveis mudanças a serem feitas no futuro para customização e não customizada elementos

Eu acho que a solução que irá satisfazer a todos nós será aquela combinando algumas dessas opções como _etapas_, adição de API, aviso e desaprovação ou mudança de comportamento.

Talvez a opção 5, com aviso, a opção 2 posterior com a depreciação do wrapper necessário.

Talvez a opção 5, com aviso, a opção 2 posterior com a depreciação do wrapper necessário.

Na verdade, estava pensando que fazer o oposto seria uma série de etapas melhor. Opção 1 ou 2 porque é uma mudança menos dramática. E medir como isso acontece e como a história do SSR começa a parecer para os componentes da web. Em seguida, continuando com a opção 5, pois ela adiciona uma nova API e uma noção de componentes proxy / wrapper.

O principal problema com a opção 1 é que é uma grande quebra de BC. O principal problema com a opção 2 é que ela tem uma condição de corrida dependendo de quando o elemento é atualizado. Não tenho certeza se qualquer um desses é realmente aceitável para um projeto que é tão amplamente usado como o React é.

Dada a disponibilidade da opção 5, eu me pergunto se podemos, em vez disso, fazer melhorias muito mais seguras, mas úteis, para usos fora da opção 5. Por exemplo, que tal o inverso da opção 4: simplesmente introduza domProperties e eventListeners props, para que você possa fazer coisas como:

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

Isso é totalmente compatível com versões anteriores, porque em virtude de suas letras maiúsculas, domProperties e eventListeners não são nomes de atributos válidos . Exceto que o React 16 atualmente chama setAttribute () até mesmo para nomes de propriedades com alfas superiores, contando com os navegadores para colocar o nome em minúsculas internamente; no entanto, poderia uma futura versão secundária do React 16 emitir um aviso ao encontrar adereços de elementos personalizados que não são nomes de atributos válidos, para que as pessoas possam corrigir seus erros de caixa antes de atualizar para o React 17?

Além de preservar o BC, essa abordagem é fácil de entender: é centrada em atributos (o que significa que os adereços do React são tratados como atributos de elemento), o que se ajusta dado que o nome do próprio elemento é todo em minúsculas e tem um travessão e, portanto, é centrado em HTML. No entanto, domProperties é fornecido para os casos relativamente raros em que você precisa passar propriedades, como passar dados ricos.

Para pessoas que desejam mudar seu modelo mental para ser centrado na propriedade (ala Opção 1), é aí que uma API de estilo de opção 5 pode entrar:

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

Com esta sintaxe, cada objeto é tratado como uma propriedade (opção 1). O que se encaixa, porque o elemento "nome" (XFoo) também segue uma convenção centrada em JS. Acho que ainda queremos, no mínimo, oferecer suporte a data-* e aria-* props tratados como atributos, que podemos limitar a apenas esses, ou generalizar para tratar qualquer prop com um travessão como um atributo.

Enquanto isso, para oferecer suporte à configuração da opção 5 de SSR, poderíamos adicionar uma API de configuração a ReactDOM.customElement , como:

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

Talvez ssrConfiguration pudesse ser uma função de retorno de chamada semelhante à ReactDOM.setAttribute one no comentário de @treshugart ?

O que você acha?

@effulgentsia Gosto de para onde estão indo seus pensamentos. Trocando um pouco os nomes de bicicleta: domProps / domEvents . Isso está muito próximo da opção 4.

WRT SSR, acho que o SSR pode ser manipulado pelos próprios elementos personalizados, desde que o React possa respeitar os atributos que o componente sofre mutação se não os estiver rastreando. Eu postei uma essência antes, mas aqui está novamente, por conveniência: https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6. Essencialmente, o componente aplica atributos após renderizar no servidor e os reidrata no cliente. O SSR para componentes da web é possível, mas as soluções ainda estão sendo discutidas no nível dos padrões, então talvez seja melhor aguardar nesta parte da proposta.

@effulgentsia Também gosto de para onde você está indo. @sophiebits , @gaearon do
vocês têm pensamentos nesta direção?

Na terça-feira, 31 de outubro de 2017, 19h33, Trey Shugart [email protected] escreveu:

@effulgentsia https://github.com/effulgentsia Gosto de onde você
pensamentos estão indo. Trocando um pouco os nomes de bicicleta, pode ser útil para
alinhe sua nomenclatura: domProps / domEvents ou algo assim.

Opção 2 do WRT, é pelo menos compatível com versões anteriores e resolve a maioria dos casos de uso,
mas estou voltando para as alternativas.

Acho que o SSR pode ser tratado pelos próprios elementos personalizados, desde que
O React pode respeitar os atributos que o componente sofre mutação em si mesmo se for
não rastreá-los. Eu postei uma ideia antes, mas aqui está de novo, para
conveniência:
https://gist.github.com/treshugart/6eff0da3c0bea886bb56589f743b78a6.
Essencialmente, o componente aplica atributos após renderizar no servidor
e os reidrata no cliente. SSR para componentes da web é possível, mas
soluções ainda estão sendo discutidas em nível de padrões, então pode ser
melhor esperar nesta parte da proposta aqui.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/facebook/react/issues/11347#issuecomment-340960798 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/ABBFDeiQhBWNGXNplbVV1zluYxT-ntFvks5sx9hngaJpZM4QD3Zn
.

Bikeshedding os nomes um pouco: domProps / domEvents.

Eu gostei daqueles. Também estou pensando se há uma maneira de torná-los ainda mais idiomáticos para React, substituindo o conceito de "dom" pelo conceito de "ref". Em outras palavras: refProps / refEvents , já que eles tratam de anexar props e ouvintes de eventos ao "ref".

E então pensei, e se em vez de introduzir novos nomes especiais dentro de this.props , simplesmente sobrecarregássemos o atributo ref JSX existente. Atualmente, "ref" é uma função chamada quando o componente React é montado e desmontado. E se permitíssemos que também fosse um objeto:

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

Apenas uma ideia.

Eu realmente gosto dessa abordagem :)

Ping amigável sobre isso. @gaearon @sophiebits , vocês têm alguma opinião sobre a última proposta de @effulgentsia ? Apenas curioso se está no estádio ou não.

Acabamos de abrir um processo RFC. Podemos pedir a algum de vocês que o envie como RFC?
https://github.com/reactjs/rfcs

Prefiro não adicionar domProperties e eventListeners "props" (ou o equivalente em um objeto ref = {{}}) porque isso torna o uso de elementos personalizados muito pouco natural e diferente de todos os outros componentes React. Se o usuário do componente precisar saber exatamente a diferença entre propriedades e atributos, etc, então eu preferiria fazer isso como uma solução no estilo ReactDOM.createCustomElementType. Então, se você usar o componente exatamente uma vez, terá uma quantidade comparável de trabalho (especificar a configuração e usá-la uma vez), mas se você usar o componente muitas vezes, não precisará pensar no objeto de configuração todas as vezes. Exigir que a configuração seja especificada todas as vezes parece frustrar os objetivos de ter uma integração limpa de elementos personalizados, a menos que esteja faltando alguma coisa.

@sophiebits OK, eu poderia tentar escrever um RFC para algo assim. O que você acha sobre a ideia que surgiu em 26 de

Pelo menos com o estilo createCustomElementType , as bibliotecas podem mapear facilmente suas APIs para isso. Ou seja, nosso skatejs / renderer-react poderia facilmente pegar props e configurar o elemento customizado para React. No entanto, isso deixa o pessoal baunilha alto e seco, sem nenhuma abstração ou trabalho. Eu gosto da sugestão de Rob de um padrão seguro, permitindo um controle refinado. Isso é algo que funcionaria?

@robdodson acho que

A opção 3 é a melhor opção até agora e pode funcionar com SSR, vou explicar como.

Até agora, todas as opções sozinhas, exceto 3,

  • criará alta carga de manutenção para todos fora da fonte React
  • ou eles forçam todos os autores de elementos personalizados em todo o universo a obedecer às regras específicas do React.

Aqui estão as regras universais com as quais todos concordamos:

  • definir atributos com setAttribute é a forma mais padrão no universo de passar dados para elementos de uma forma que corresponda 1 a 1 com atributos HTML declarativos. Isso tem que funcionar 100% do tempo como uma lei do universo, portanto, é a única maneira 100% garantida de passar dados para os elementos.
  • algumas pessoas não gostam que ele tenha sido projetado apenas para cordas.
  • Alguns elementos (repito, _apenas alguns elementos_ ), aceitam valores por meio de propriedades de objeto que mapeiam para certos atributos. Isso _não_ é algo em que se pode confiar 100% do tempo.
  • algumas pessoas gostam de propriedades de objetos porque podem aceitar não strings

Portanto, no mínimo, se o React quiser trabalhar 100% com _cada elemento personalizado no universo e não ser um fardo para as pessoas_ , então:

  • React precisa perceber que não é Deus, há muitas outras bibliotecas que as pessoas usam além de react,
  • portanto, o React deve _por padrão_ apenas passar os dados via setAttribute porque isso é 100% padrão.
  • O React deve aceitar o fato de que os autores de elementos personalizados podem estender / sobrescrever os métodos setAttribute em suas definições de classe, fazendo setAttribute aceitar outras coisas além de strings. Um bom exemplo pode ser bibliotecas como A-frame.
  • React deve aceitar que se um autor de elemento personalizado deseja que um elemento personalizado funcione com todas as bibliotecas possíveis, _não apenas com React_, então esse autor dependerá de setAttribute para tornar seu elemento compatível por padrão com tudo, e se por padrão todas as bibliotecas dependem de atributos, então todo o universo trabalhará em conjunto. _Não há ifs, ands ou buts sobre isso! _ (A menos que w3c / whatwg faça grandes mudanças)

Entããããão , dito isso, devemos no mínimo

Então, podemos pensar sobre as implicações de elementos às vezes tendo API de propriedade de objeto:

  • os desenvolvedores têm a opção de usar setAttribute sabendo que funcionará o tempo todo.
  • os desenvolvedores às vezes podem tirar proveito das propriedades do objeto.
  • os desenvolvedores sempre devem estar cientes da existência ou não de uma interface de propriedade de objeto para qualquer elemento (customizado ou não).
  • interfaces de propriedade de objeto são uma interface alternativa que não necessariamente mapeia 1 para 1 com atributos.

Portanto, com este conhecimento de atributos vs propriedades, uma solução em React que _deseja aumentar o padrão de 100% e respeitar as leis do universo_ deve:

  1. permite que os atributos funcionem 100% do tempo por padrão. Isso significa que <x-foo blah="blah" /> deve _por padrão_ mapear para setAttribute e passar o valor ao longo de _não alterado_ . Esta é uma mudança ininterrupta. Na verdade, é uma alteração fixa que, de outra forma, resultaria na transmissão de uma string "[object Object]" sem sentido.
  2. Crie uma maneira alternativa de permitir que o usuário React

Parece que a Opção 3, usar um sigilo (cuja sintaxe extra honestamente não é difícil de aprender), é uma solução que chega mais perto do ideal. Com base nesta pergunta do SO , então o único símbolo disponível é = , embora se decidir por algo como & (com uma forma escapável, talvez como \& ) seja mais legível. Por exemplo, se eu quiser um acessório especificamente:

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

A maioria dos outros caracteres cobertos pela especificação de sintaxe HTML WHATWG deve funcionar, e espero que funcionem, mas esse é outro tópico.

A opção 3 é a melhor opção até agora.

Se a hidratação não está sendo usada no cliente e, portanto, os adereços não fazem sentido no SSR, então, tudo bem. Nunca funcionou antes e não precisa funcionar agora. O SSR no estilo PHP ou no estilo Java envia HTML estático sem hidratação e confia 100% nos atributos. Ou seja, se usarmos React SSR, provavelmente usaremos hidratação do lado do cliente, e se não quisermos hidratação, devemos simplesmente estar cientes do fato de que não devemos usar adereços neste caso. _Isso é simples. Tudo o que precisa fazer é deixar essa advertência clara na documentação._

Mas!!! Isso não é tudo! Ainda podemos incluir os recursos da Opção 5 para dar às pessoas mais controle. Com uma API como a Opção 5,

  • Algumas pessoas podem configurar como alguns atributos podem ser mapeados para adereços
  • E como alguns adereços podem mapear os atributos. Isso pode ajudar as pessoas a fazerem o SSR funcionar, mesmo para o tipo de SSR não hidratante!
  • podemos deixar as pessoas definirem "se este atributo for escrito, realmente passe para este prop, mas se for SSR, não passe para o prop", ou "passe este prop para este atributo apenas durante SSR, caso contrário, faça o que Eu especifiquei usando o sigilo ", etc

No final das contas, o seguinte parece uma solução que funcionaria:

  • Opção 3 com setAttribute usado por padrão nos bastidores,
  • com uma correção para # 10070 para que o React não atrapalhe as pessoas,
  • e uma API como na Opção 5 para ajuste adicional para aqueles que realmente precisam, incluindo maneiras de ajustar SSR, cliente ou ambos.

Feliz Ano Novo!

Eu quero responder a alguns dos pontos acima, mas temo que este tópico já esteja _incrivelmente_ longo. Então, desculpe por torná-lo mais longo: P

Aqui estão as regras universais com as quais todos concordamos:

definir atributos com setAttribute é a maneira mais padrão no universo de passar dados para elementos de uma forma que corresponda 1 para 1 com atributos HTML declarativos. Isso tem que funcionar 100% do tempo como uma lei do universo, portanto, é a única maneira 100% garantida de passar dados para os elementos.

Isso não é totalmente verdade. Não há nada na plataforma que impeça que um elemento personalizado exponha uma interface de atributos. Você pode facilmente criar um que aceite apenas propriedades JS. Portanto, não é uma "maneira garantida de passar dados". A falta de mecanismos de aplicação significa que você não pode confiar em nenhum dos estilos (atributos HTML ou propriedades JS) com 100% de certeza.

algumas pessoas não gostam que ele tenha sido projetado apenas para cordas.
Alguns elementos (repito, apenas alguns elementos), aceitam valores por meio de propriedades de objetos que mapeiam para determinados atributos. Isso não é algo com que se possa confiar 100% do tempo.

Nós encorajamos as pessoas a nem mesmo se preocupar em criar atributos para propriedades que aceitam dados ricos como objetos / matrizes. Isso ocorre porque alguns dados não podem ser serializados de volta para uma string de atributo. Por exemplo, se uma das propriedades do seu objeto for uma referência a um Nó DOM, isso não pode realmente ser stringificado. Além disso, quando você restringe e analisa novamente um objeto, ele perde sua identidade. Ou seja, se houver uma referência a outro POJO, você não pode realmente usar essa referência, pois criou um objeto inteiramente novo.

algumas pessoas gostam de propriedades de objetos porque podem aceitar não strings

portanto, o React deve, por padrão, apenas passar os dados por meio de setAttribute, porque isso é 100% padrão.

As propriedades do JavaScript são igualmente padrão. A maioria dos elementos HTML expõe uma interface de atributos e propriedades correspondentes. Por exemplo, <img src=""> ou HTMLImageElement.src .

O React deve aceitar o fato de que os autores do Elemento Personalizado podem estender / substituir os métodos setAttribute em suas definições de classe, fazendo com que setAttribute aceite outras coisas além de strings.

Os autores poderiam fazer isso, mas na verdade parece muito mais "fora do padrão" do que apenas usar propriedades JS. Também pode expor você a problemas estranhos relacionados à análise e clonagem do elemento .

O React deve aceitar que se um autor de elemento personalizado deseja que um elemento personalizado funcione com todas as bibliotecas possíveis, não apenas com o React, então esse autor dependerá de setAttribute para tornar seu elemento compatível por padrão com tudo, e se por padrão todas as bibliotecas dependem de atributos , então todo o universo trabalhará em conjunto. Não há ifs, ands ou buts sobre isso! (a menos que o w3c / whatwg faça grandes mudanças)

Não tenho certeza de como você chegou a essa conclusão porque existem outras bibliotecas que preferem definir propriedades JS em elementos personalizados (Angular, por exemplo). Para compatibilidade máxima, os autores devem apoiar seus atributos com propriedades JS. Isso cobrirá a maior área de superfície de usos possíveis. Todos os elementos criados com o Polymer fazem isso por padrão.

permite que os atributos funcionem 100% do tempo por padrão. Isso significa que deve, por padrão, mapear para setAttribute e passar o valor inalterado. Esta é uma mudança ininterrupta. Na verdade, é uma alteração fixa que, de outra forma, resultaria na transmissão da string "[object Object]" sem sentido.

Acho que o React na verdade _está passando o valor inalterado. Chamar setAttribute('foo', {some: object}) resulta em [object Object] . A menos que você esteja propondo que eles chamem JSON.stringify() no objeto? Mas então esse objeto não é "inalterado". Eu acho que talvez você esteja contando com o autor para substituir setAttribute() ? Pode ser mais plausível encorajá-los a criar propriedades JS correspondentes em vez de fazer o patching do DOM.

Acho que o React na verdade _está passando o valor inalterado. Chamar setAttribute('foo', {some: object}) resulta em [object Object]

React está forçando valores para uma string antes de passar para setAttribute :

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

e

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

Basicamente, concordo com tudo o que você disse.

Concordamos que as pessoas estão fazendo as coisas das duas maneiras, e não há um padrão que obrigue todos a fazerem de uma forma ou de outra, então ainda acho

  • Opção 3 com setAttribute usado por padrão e um sigilo para especificar o uso de propriedades de instância,
  • com uma correção para # 10070 para que o React não force args a strings,
  • e a Opção 5, uma API para ajuste fino

Se a Opção 5 for bem feita, a hidratação para soluções SSR pode mapear os dados para atributos ou props, conforme pode ser especificado pelo uso da Opção 5 API.

React está forçando valores para uma string antes de passar para setAttribute

Entendo. Como a maioria das pessoas não define um toString() , o padrão é [object Object] .

Como a maioria das pessoas não define um toString() , o padrão é [object Object] .

Como se eu fizesse

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

o resultado é

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

Obviamente, como desenvolvedores da Web, devemos estar cientes do que acontece quando passamos um não-strings para os atributos do elemento. Por exemplo, estou ciente de que posso passar não strings para elementos A-Frame e devo estar livre para fazer isso sem que uma biblioteca atrapalhe.

React precisa perceber que não é Deus, existem muitas outras bibliotecas que as pessoas usam além de react

Isso é desnecessariamente sarcástico. Você notará neste tópico que nos preocupamos com isso, mas que existem muitas opções e visões diferentes de onde levar o design de elemento personalizado. Certamente não é óbvio o que deve ser feito.

@sebmarkbage Desculpe por isso, eu não tive a

O que eu quis dizer é que o React é muito popular, então o React tem o potencial de influenciar as pessoas a fazerem coisas de uma determinada maneira que pode não funcionar em outros lugares (por exemplo, poderia dizer às pessoas para confiar em propriedades de instância para todos os elementos personalizados que não para trabalhar com todos os elementos personalizados).

Atualmente, o React converte todos os valores passados ​​aos atributos do elemento em strings. Se o React não tivesse feito isso, por exemplo, não haveria necessidade da existência de um reagente-frame (que contorna o problema da string).

Se o React puder apenas nos permitir fazer qualquer escolha sobre como passamos os dados para os elementos, assim como no JavaScript simples, isso me deixaria o usuário mais satisfeito. 😊

Novamente, desculpe a minha escolha de palavras lá. Vou pensar duas vezes na próxima vez.

Eu adicionei um comentário ao RFC PR para isso. Acho que vale a pena discutir, pois cobre o que está sendo proposto, bem como um modelo mais simples para inferir de forma confiável um elemento personalizado e suas propriedades. Ele o transforma de volta em uma abordagem híbrida, mas oferece uma maneira de integração zero-config para a maioria dos casos de uso.

Estou ansioso para ver esse recurso implementado no React. Muito obrigado por seus esforços para tornar o React excelente.

Alguma atualização neste RFC?

As bibliotecas de elementos personalizados estão ficando muito boas e eu adoraria usá-las em meu aplicativo React. Alguma notícia sobre isso? Empacotar elementos personalizados e restringir seu conteúdo para depois analisá-los novamente é uma solução bastante impraticável, considerando que o Vue e o Angular estão lidando com componentes nativamente com facilidade

Alguma atualização sobre este problema?

Eu também adoraria usar bibliotecas de elementos personalizados sem recorrer a hacks. Eu adoraria que esse problema fosse resolvido.

@ mgolub2 A equipe React não se preocupa com o que a comunidade da web deseja. Componentes da Web são agora um padrão amplamente suportado e não há nenhum tipo de sinal da equipe para oferecer suporte a esse padrão, após 2 anos.

Olá a todos, comecei uma solicitação pull para corrigir esse problema alterando duas linhas: https://github.com/facebook/react/pull/16899

Isso permite que um autor de elemento personalizado faça algo como o seguinte:

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) { /*...*/ }
  }
}

Há muitas variações de como um método setAttribute estendido poderia se parecer, esse é apenas um pequeno exemplo.

Equipe React, você pode argumentar que os autores de elementos personalizados não deveriam fazer isso, porque eles contornam o tratamento de atributo nativo do DOM em alguns casos (por exemplo, quando os valores não são strings). Se você tem essa opinião, isso ainda não significa que você deve impedir o que os autores de elementos personalizados podem fazer com seus elementos __ personalizados __.

O React não deve emitir opiniões sobre como as APIs DOM existentes são usadas. O React é uma ferramenta para manipular o DOM e não deve ser opinativo sobre o que podemos fazer com o DOM, apenas de que forma os dados fluem para o DOM. Por "forma como os dados fluem para o DOM", quero dizer a rota que os dados percorrem para chegar lá, sem mutação nos dados (converter os objetos de um autor em strings é alterar os dados do autor).

Por que você deseja alterar os dados do autor? Por que você não pode simplesmente presumir que a pessoa que deseja manipular o DOM sabe quais dados passar para o DOM?

@trusktr Acho que isso foi discutido em https://github.com/facebook/react/issues/10070

Eu realmente alertaria as pessoas contra dizer aos autores de elementos personalizados para substituir um método integrado como setAttribute . Eu não acho que seja para nós consertá-lo. cc @gaearon

É inofensivo sobrescrever setAttribute em subclasses como essa (isso não é monkey patching). Mas, como mencionei, por que o React tem que ditar isso, se esse não é necessariamente o trabalho da biblioteca React. Queremos usar o React para manipular o DOM (sem impedimentos).

Se eu tiver que usar el.setAttribute() manualmente para aumentar o desempenho, isso também tornará a experiência de desenvolvimento pior.

Não acho que a equipe do React esteja salvando muitas pessoas de grandes perigos ao converter qualquer coisa passada em setAttribute em strings.

Eu concordo que outra solução pode ser melhor. Por exemplo, atualizar a especificação JSX para ter alguma nova sintaxe, mas isso parece demorar.

O que a comunidade perde se retirarmos a conversão automática de strings? O que a equipe React perde?

A equipe React poderia melhorar a situação mais tarde, com uma solução melhor ...

Por que não simplesmente nos dar a opção de contornar a estringificação? É algo que você gostaria de considerar?

Eu realmente alertaria as pessoas contra dizer aos autores de elementos personalizados para substituir um método integrado como setAttribute .

Você pode fornecer um bom motivo para isso?

Isso parece uma pista falsa. "Convencer todos os que escrevem elementos personalizados a anexar um método com comportamento específico a seu elemento" não é uma solução para esse problema, que é sobre como definir propriedades em elementos DOM.

@trusktr

Você pode fornecer um bom motivo para isso?

Os componentes da Web são um padrão. Portanto, os derivados do padrão também devem ser compatíveis com o padrão.

No entanto, substituir setAttribute não se encaixa nesta condição: ele cria um hack apenas para React, embora haja muitos outros frameworks que funcionam com componentes da web prontos para uso. Portanto, eles precisariam consumir o hack React mesmo quando não funcionassem com o React. Não acho que seja a solução certa.

Em seguida, é sabido que corrigir os métodos padrão é uma abordagem errada. Substituir setAttribute altera o comportamento original que pode confundir os usuários finais ao tentar usá-lo. Todos esperam que o padrão funcione como padrão, e o elemento personalizado não é exceção porque herda o comportamento HTMLElement . E embora possa funcionar com o React, ele cria uma armadilha para todos os outros usuários. Por exemplo, quando os componentes da web são usados ​​sem uma estrutura, setAttribute pode ser muito chamado. Duvido que os desenvolvedores de elementos customizados concordem em usar essa abordagem com o pé direito.

Pessoalmente, acho que algum tipo de invólucro do React parece muito mais promissor.

Ei pessoal, enquanto esperamos, criei um shim para envolver seu componente da web em 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>
  </>
)

Espero que isso seja útil

(Esta é minha primeira incursão em OSS, e uma das primeiras fontes abertas de algo fora do meu escritório. Críticas construtivas são mais do que bem-vindas 😄)

Ei pessoal, enquanto esperamos, criei um shim para envolver seu componente da web em 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>
  </>
)

Espero que isso seja útil

(Esta é minha primeira incursão em OSS, e uma das primeiras fontes abertas de algo fora do meu escritório. Críticas construtivas são mais do que bem-vindas 😄)

Ótimo invólucro :)

Também vale a pena mencionar que construir componentes da web com Stencil corrige esse problema com React: https://stenciljs.com/docs/faq#can -data-be-passes-to-web-components-

@matsgm Estou feliz que construir com o Stencil funcionou para você. No entanto, como uma palavra de cautela, em nossa experiência, o Stencil provou não funcionar bem com outras estruturas de componentes da web, especificamente o Polymer, e ficamos incomodados com meia dúzia de outros problemas entre suas ferramentas de construção, suporte e funcionalidade geral. Sua milhagem pode variar :)

Só estou tentando me atualizar com este tópico. Qual é a solução final?

De certa forma, sou um grande fã da definição explícita de atr, prop e evento, em vez de heurísticas mágicas, que podem ser muito confusas.

Por exemplo, snabbdom usa namespacing de nível superior explícito, o que torna mais fácil saber o que vai para onde. Não há manipulação de string que seja mais rápida de executar, por exemplo, sufixo de corte de onClick => click ainda é um sucesso de desempenho.

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

Desejo que JSX suporte namespacing com sintaxe de ponto. Isso significa que os props seriam o namespace padrão e poderíamos simplesmente escrever

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

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

Isso é possível modificando o analisador React JSX Pragma.

A opção adicional é manter propeties (ou props para os fãs de reação) como uma palavra designada para transferência de propriedade

Como o 17.0 foi lançado hoje, podemos atualizar esse problema para refletir o status de quando isso será resolvido?

Sim. Originalmente, planejamos que o React 17 fosse um lançamento com muito mais depreciações removidas (e com novos recursos). No entanto, não é muito realista que as bases de código antigas atualizem todo o seu código, e é por isso que decidimos lançar o React 17, que se concentra apenas em uma única alteração significativa (os eventos são anexados ao elemento raiz em vez do documento ) Isso é para que os aplicativos antigos possam permanecer no React 17 para sempre e atualizar apenas partes deles para maiores de 18 anos.

Sei que é frustrante dizermos que esse recurso específico iria para o 17, mas você notará que quase todas as coisas que planejamos originalmente para o 17 foram movidas para o 18 também. 17 é um lançamento especial de trampolim. O que nos permite fazer alterações de interrupção mais agressivas em 18. Possivelmente incluindo este, se houver um caminho claro a seguir.

Não tenho certeza de qual é o consenso mais recente da comunidade WC sobre qual dessas opções é preferível. É muito útil ter todos eles anotados (grandes adereços para @robdodson por fazer esse trabalho). Estou curioso para saber se as opiniões das pessoas sobre essas opções evoluíram desde que este tópico foi escrito e se há alguma informação nova que possa nos ajudar a escolher a direção.

Não acho que a comunidade WC mudou sua opção preferida, que ainda é a opção 3. @developit pode falar mais sobre como o Preact é compatível com elementos personalizados, o que pode ser interessante para o React também. Para uma visão geral de como as estruturas são compatíveis com a passagem de dados (complexos) em elementos personalizados, https://custom-elements-everywhere.com/ tem todos os detalhes.

Observe que em https://github.com/vuejs/vue/issues/7582 , Vue escolheu usar um sigilo e escolheram "." como um prefixo (não o "@" do Glimmer).

Em https://github.com/vuejs/vue/issues/7582#issuecomment -362943450, @trusktr sugeriu que a implementação de SSR mais correta seria não renderizar propriedades sigiladas como atributos no HTML SSR, e em vez disso, defina-os como propriedades por meio de JS durante a hidratação.

Acho muito improvável que introduzamos a nova sintaxe JSX apenas por causa desse recurso específico.

Também há uma questão de saber se vale a pena fazer a opção (3) se uma versão mais genérica da opção (5) estiver disponível. Ou seja, a opção (5) pode ser um mecanismo de baixo nível para declarar nós React customizados de baixo nível com comportamento customizado de montagem / atualização / desmontagem / hidratação. Nem mesmo específico para elementos personalizados per se (embora eles sejam um dos casos de uso).

Em vez de introduzir uma sintaxe JSX totalmente nova para esse recurso específico, que tal introduzir um JSX mais geral e usá-lo para definir propriedades personalizadas?

Propus adicionar a sintaxe de propriedade computada do ECMAScript ao JSX há muito tempo (facebook / jsx # 108) e acho que seria uma adição útil à sintaxe em geral.

Se a sintaxe de propriedade computada estivesse disponível, isso deixaria em aberto a possibilidade de definir propriedades usando a sintaxe de propriedade computada e símbolos ou strings prefixadas.

Por exemplo:

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

@dantman Como esta proposta trata a renderização e hidratação do servidor?

Não acho que ninguém na comunidade WC queira a opção 5.

Realmente não é diferente da prática atual de patching escrevendo um wrapper React customizado para elementos customizados. A grande desvantagem dessa abordagem é que ela sobrecarrega o autor do componente para o React em caso especial ou o consumidor do componente para os elementos personalizados do caso especial, nenhum dos quais deve ser necessário.

Qual é a sua opinião sobre renderização e hidratação do servidor? Se não houver configuração explícita, qual heurística é desejável? Houve uma consolidação em como isso geralmente é feito na comunidade WC? Eu reli este RFC e não parece entrar em detalhes sobre este tópico. Mas é muito importante, especialmente à medida que o React passa a dar mais ênfase à renderização do servidor (já que é freqüentemente criticado por padrões muito centrados no cliente).

@gaearon Não conheço propriedades de hidratação e customização suficientes para saber o que é necessário; esta é principalmente uma proposta de sintaxe.

A ideia geral é que property(propertyName) poderia, dependendo de como você deseja implementá-lo, gerar uma string prefixada (por exemplo, '__REACT_INTERNAL_PROP__$' + propertyName ) ou criar um símbolo e salvar uma associação "símbolo => nome_da_properdade" em um mapa.

O último pode ser problemático se você precisar comunicar esse mapa ao cliente.

No entanto, para meu entendimento aproximado, propriedades não são algo que você possa manipular no servidor e a hidratação envolve o código do cliente, então não tenho certeza de qual é o plano para isso. Quanto à minha proposta, provavelmente pode ser adaptada a quaisquer planos que você tenha para resolver esse problema. Se há algo que você planeja fazer com as propriedades no servidor, ele pode fazer isso quando vir uma das propriedades criadas por property .

Como autor de uma biblioteca de componentes da web, a opção 5 não é uma escolha conveniente. Quero criar elementos personalizados sem precisar definir explicitamente um esquema para cada estrutura e componente. E muitos autores de elementos personalizados simplesmente não fazem isso, empurrando a carga para o desenvolvedor React.

Ninguém ganha com a opção 5. 😕

@claviska Você tem alguma opinião sobre como a renderização e hidratação do servidor devem funcionar com abordagens mais implícitas?

@gaearon SSR será subdividido em duas situações: aquelas em que o elemento personalizado suporta SSR e aquelas em que não.

Para elementos que não oferecem suporte a SSR, definir propriedades no servidor não importa. Exceto para propriedades refletivas integradas como id e className , eles podem ser descartados e gravados apenas no cliente.

Os elementos que oferecem suporte às propriedades SSR são importantes, mas apenas para que possam acionar qualquer resultado que causem no DOM. Isso requer uma instância de elemento ou algum tipo de proxy / substituto SSR e algum protocolo para comunicar o estado DOM com SSR. Ainda não existe uma interface do lado do servidor comum para esse processo, portanto, não há realmente nada a ser feito aqui por enquanto. Os autores de componentes da Web e mantenedores de bibliotecas precisarão descobrir algumas coisas antes que seja viável para o React construir quaisquer pontes lá.

No trabalho da minha equipe no SSR, integramos o React corrigindo createElement e usando dangerouslySetInnerHTML . Acho que esse é o nível de integração / experimentação que estaremos por um tempo. Em algum ponto em breve, espero que possamos convergir em alguns protocolos de usuário-terreno para interoperabilidade dentro de sistemas SSR. Até então, é perfeitamente seguro e prudente ter propriedade de elemento personalizado e suporte a eventos sem SSR _deep_. A tag do elemento ainda seria SSR como filha de um componente React como é hoje.

Exceto para propriedades de reflexão integradas como id e className, eles podem ser descartados e gravados apenas no cliente.

Você pode me ajudar a entender como isso funciona? Por exemplo, digamos que há <github-icon iconname="smiley" /> . Você quer dizer que o SSR deve apenas incluir <github-icon /> na resposta HTML e, em seguida, o React definirá domNode.iconname = ... durante a hidratação? Nesse caso, não tenho certeza se entendi o que acontece se a implementação do elemento personalizado carregar antes de ocorrer a hidratação do React. Como a implementação de github-icon saberá qual ícone processar se iconname não existir no HTML?

Ainda não existe uma interface do lado do servidor comum para esse processo, portanto, não há realmente nada a ser feito aqui por enquanto. Os autores de componentes da Web e mantenedores de bibliotecas precisarão descobrir algumas coisas antes que seja viável para o React construir quaisquer pontes lá.

Estou curioso para saber se há algo em particular que deve acontecer para a comunidade formar um consenso aqui. React é quem está segurando isso? Compreendo muito bem a frustração associada ao fato de essa questão estar aberta desde 2017. Por outro lado, já se passaram três anos e acho que você está dizendo que esse consenso ainda não se formou. Quais são os pré-requisitos para que isso aconteça? É apenas uma questão de mais experimentação?

@gaearon se a implementação do elemento personalizado carregar antes de ocorrer a hidratação do React, ele atribuirá qualquer valor padrão do atributo iconname . O que você quer dizer com if _iconname_ does not exist in the HTML ? Se o tipo HTMLElement não tiver um atributo definido, ele o ignorará. assim que a definição do elemento personalizado for carregada, ela estenderá o tipo HTMLElement e definirá iconname e será capaz de reagir a um novo valor que está sendo passado.

Estou tentando entender isso da perspectiva do usuário. Estamos renderizando uma página no servidor. Possui um ícone no meio de um texto. Esse ícone é implementado como um elemento personalizado. Qual é a sequência de coisas que o usuário final deve experimentar?

Meu entendimento até agora é que:

  1. Eles veem o resultado inicial da renderização. Ele incluirá toda a marcação normal, mas eles não verão o elemento personalizado <github-icon> . Provavelmente seria um buraco, como um div vazio. Por favor me corrija se eu estiver errado (?). Sua implementação ainda não foi carregada.

  2. A implementação do elemento personalizado <github-icon> carregada e é registrada. Se bem entendi, este é o processo denominado "atualização". No entanto, mesmo que seu JS esteja pronto, ele ainda não pode mostrar nada porque não incluímos iconname no HTML. Portanto, não temos as informações de qual ícone renderizar. Isso ocorre porque dissemos anteriormente que "propriedades não integradas podem ser eliminadas do HTML". Portanto, o usuário ainda vê um "buraco".

  3. React e o código do aplicativo é carregado. A hidratação acontece. Durante a hidratação, o React define a propriedade .iconname = acordo com a heurística. O usuário pode ver o ícone agora.

Este é um detalhamento correto para o caso em que a implementação JS do elemento personalizado carrega primeiro? Este é o comportamento desejável para a comunidade WC?

@gaearon sim, isso é o que eu esperava que acontecesse nesta situação.

Por outro lado, já se passaram três anos e acho que você está dizendo que esse consenso ainda não se formou

Não estou dizendo que o consenso não foi alcançado. Estou dizendo que você não precisa se preocupar com _deep_ SSR agora e não deve permitir que preocupações vagas sobre isso bloqueiem a interoperabilidade mais básica do lado do cliente que pode ser realizada e extremamente útil agora.

Eu não vi essa distinção antes (profundo vs superficial), então deixe-me tentar reformulá-la para verificar se eu entendi você.

Por SSR "profundo", acho que você quer dizer SSR semelhante a como funciona nos componentes React. Ou seja, renderizar um CE produziria uma saída que pode ser exibida antes que a lógica do CE seja carregada. Isso é algo que você está dizendo que não devemos

Por SSR "não profundo", acho que você está dizendo que apenas colocamos a própria tag CE em HTML. Sem se preocupar com o que vai resolver. Isso também faz sentido para mim. Minha pergunta era sobre _este_ fluxo - não sobre o SSR "profundo".

Em particular, https://github.com/facebook/react/issues/11347#issuecomment -713230572 descreve uma situação em que mesmo quando _já temos_ a lógica CE baixada, ainda não podemos mostrar nada ao usuário até a hidratação. Torna-se que suas propriedades são desconhecidas até que todo o código do cliente JS do aplicativo seja carregado e a hidratação aconteça. Estou perguntando se esse é realmente o comportamento desejado.

Não acho que seja uma preocupação vaga da minha perspectiva. Não quero interpretar mal a solicitação de recurso. Portanto, obter a semântica exata especificada me ajudaria a avaliar esta proposta. Por exemplo, uma preocupação prática é que o React não difere atualmente os atributos / propriedades na produção durante a hidratação, porque nenhum dos componentes embutidos precisa disso. Mas é algo que poderíamos adicionar neste caso específico, se for realmente necessário.

@gaearon

Acho que https://github.com/facebook/react/issues/11347#issuecomment -713230572 pode não ser o melhor exemplo de caso em que esse recurso é necessário.

Pelo menos em minha experiência, definir propriedades em vez de atributos não é tão necessário para tipos mais simples como strings, números ou booleanos.

O caso que você descreve poderia ser facilmente alcançado apenas usando iconname como um atributo direto e ainda seria basicamente o mesmo resultado final renderizado sem esperar pela hidratação.

oa implementação do elemento personalizado é carregada e registrada. Se bem entendi, este é o processo denominado "atualização". No entanto, mesmo que seu JS esteja pronto, ele ainda não pode mostrar nada porque não incluímos o nome do ícone no HTML. Portanto, não temos as informações de qual ícone renderizar. Isso ocorre porque dissemos anteriormente que "propriedades não integradas podem ser eliminadas do HTML". Portanto, o usuário ainda vê um "buraco".

Quanto ao que você mencionou aqui, dependendo de como o componente é implementado você poderia evitar esse problema de ver um "buraco".

Isso pode ser alcançado simplesmente definindo padrões neste caso ou aceitando parte ou mesmo a maior parte do conteúdo por meio de slots para que o conteúdo seja exibido antes mesmo de o componente ser atualizado.


Acho que esse recurso seria útil principalmente para o uso de componentes mais complexos que têm propriedades que são Objetos, Matrizes, Funções, etc.

Tome como exemplo o componente de rolagem virtual lit-virtualizer .

Para que isso funcione corretamente, é necessário um array items e uma função renderItem e você ainda pode definir opcionalmente um Elemento scrollTarget , todos os quais só podem ser definidos no React usando refs agora mesmo.

Para um caso como este, você provavelmente carregaria o conteúdo com algum tipo de paginação ou carregamento lento de qualquer maneira, portanto, não ter nenhum conteúdo até a etapa de hidratação em um caso de SSR pode não ser tão problemático.

@gaearon Observe que os elementos personalizados são um padrão DOM e, portanto, não há uma "comunidade WC" per se, no sentido de que todos que usam elementos personalizados os usam da mesma maneira. Os elementos personalizados são integrados à sua própria maneira para cada desenvolvedor, pois são inerentemente um primitivo de baixo nível sobre o qual as pessoas se baseiam.

Dito isso, ao longo do tempo, vários padrões surgiram e todos os frameworks populares (exceto React) implementam alguma solução para fornecer compatibilidade para este padrão DOM. Como você pode ver em https://custom-elements-everywhere.com/ , todas as estruturas / bibliotecas (exceto React) escolheram opções diferentes. Nesta edição, as opções escolhidas estão listadas como opções 1, 2 e 3. Não há estrutura / biblioteca que atualmente implemente a opção 4 ou 5 e não acho que seja desejável implementá-las.

Portanto, proponho que o React siga o exemplo com as outras estruturas / bibliotecas e escolha uma opção que provou funcionar, por exemplo, entre as opções 1-3. Não acho que o React precise reinventar a roda aqui, escolhendo a opção 4 ou 5, para a qual não temos dados, é uma solução de longo prazo que pode ser mantida tanto para o React quanto para aqueles que se baseiam nos padrões DOM.

Então, pelo processo de eliminação (com os comentários vinculados abaixo), uma vez que a opção 3 não será feita para JSX e as opções 4 e 5 foram marcadas como indesejáveis ​​pelo pessoal do WC neste tópico, estamos presos a 1 ou 2 .

E como 1 parece ter desvantagens insustentáveis, parece que a opção 2 é a certa? Basta ver como o Preact faz?


Não há estrutura / biblioteca que atualmente implemente a opção 4 ou 5 e não acho que seja desejável implementá-las.

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

Acho muito improvável que introduzamos a nova sintaxe JSX apenas por causa desse recurso específico.

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

Isso faria sentido para mim, sim. Obrigado pelo resumo! 😄

Pelo menos em minha experiência, definir propriedades em vez de atributos não é tão necessário para tipos mais simples como strings, números ou booleanos. O caso que você descreve poderia ser facilmente alcançado apenas usando iconname como um atributo direto e ainda seria basicamente o mesmo resultado final renderizado sem esperar pela hidratação.

Não estou entendendo direito. Meu cenário hipotético foi uma resposta a https://github.com/facebook/react/issues/11347#issuecomment -713223628 que parecia uma sugestão de que não deveríamos emitir atributos no HTML gerado pelo servidor, exceto id e class . Não sei o que significa "apenas usar iconname como um atributo" - você está dizendo que gostaria de ver o HTML gerado pelo servidor _incluir_ outros atributos? Isso parece contradizer o que acabamos de dizer. Acho que ajudaria muito se cada resposta apresentasse um quadro completo, porque, do contrário, continuaremos andando em círculos. Minha pergunta para você é:

  1. O que exatamente é serializado no HTML gerado pelo servidor no cenário que descrevi
    uma. No caso de <github-icon iconname="smiley" />
    b. No caso que você mencionou, por exemplo, <github-icon icon={{ name: "smiley" }} />
  2. Qual DOM API é chamado no cliente durante a hidratação em qualquer um desses casos

Obrigado!

Foi considerado uma opção para criar um shim, substituindo React.createElement() por uma implementação personalizada, que faz o mapeamento de propriedades dentro?

Exemplo de uso:

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

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

Implementação de rascunho:

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

Eu sei que este não é um recurso embutido, mas deve desbloquear usuários com sobrecarga muito baixa, eu acho.

@ just-boris é basicamente o que aplicativos ou bibliotecas fazem agora, com um patch createElement ou um wrapper. Você também precisa excluir os nomes de propriedades com letras maiúsculas e minúsculas no React. Não acho que haja uma lista exportada, então eles apenas codificam um denylist como ['children', 'localName', 'ref', 'elementRef', 'style', 'className'] .

Por ser baseado em ref, as propriedades não são definidas no servidor. É por isso que chamo a preocupação de vaga. Não há como definir propriedades no React, então está tudo quebrado agora. A solução alternativa disponível já funciona apenas no cliente. Se houver uma maneira integrada de definir propriedades no cliente e não no servidor, ela não corrige ou interrompe o SSR repentinamente.

Na verdade, eu estaria mais preocupado com os eventos do que com SSR. O React não tem uma maneira de adicionar manipuladores de eventos, e isso é um grande buraco na interoperabilidade com APIs DOM básicas.

Foi considerado uma opção para criar um shim, substituindo React.createElement () por uma implementação personalizada, que faz o mapeamento de propriedades dentro?

Conforme mencionado por Rob na edição original, eu experimentei isso no passado via https://github.com/skatejs/val , portanto, ter um pragma JSX personalizado que envolve React.createElement é viável - possivelmente curto prazo - solução.

Eu também tenho o ramo WIP do Skate implementando o que foi referido como SSR profundo e raso, especificamente para React. O aprendizado deste trabalho até agora é que você não pode ter um único wrapper JSX para todas as bibliotecas se quiser fazer SSR profundo, porque cada biblioteca depende de suas próprias APIs e algoritmos para fazer isso. (Para contexto, o Skate tem um elemento personalizado central parte de sua biblioteca e pequenas camadas construídas para integrar cada biblioteca popular no mercado para tornar o consumo e o uso consistentes em todos os setores).


Não trabalho mais com código aberto, e também não trabalho mais com Twitter, então sinta-se à vontade para aceitar minha opinião com um grão de sal.

A opção 1 parece que existe como o advogado do diabo para a opção 2.

A opção 2 parece ser o caminho a percorrer. É próximo ao que Preact fez, então há precedentes e a possibilidade de um padrão da comunidade (o que eu acho que seria fantástico de fazer de qualquer maneira; JSX mostrou que isso pode funcionar), e também é um movimento pragmático para React por ser fácil de atualizar (em oposição à Opção 1).

A opção 3 parece boa no papel, mas acho que logisticamente seria muito mais difícil do que a opção 2 devido à sobrecarga cognitiva, documentação em torno dessa sobrecarga e incógnitas com SSR.

Como mencionei, originalmente apresentei a opção 4, mas após reflexão, não tenho certeza se gosto mais dela. Você poderia alterá-lo para ser mais opcional, onde especifica props e events explícitos (em vez de attrs ), mas mesmo assim a Opção 2 requer menos conhecimento dos detalhes de implementação e não há nada que alguém precise mudar para adotá-lo.

A opção 5 parece que estaríamos ignorando totalmente o problema central.


Como um aparte, estou super feliz em ver a tração nisso e espero que vocês estejam bem!

Se houver uma solução possível no userland, ela pode ser consolidada e recomendada pela comunidade por enquanto?

Seria muito mais fácil convencer a equipe principal do React a adicionar um recurso se ele já existir como um módulo de terceiros viável e popular.

Se houver uma solução possível no userland, ela pode ser consolidada e recomendada pela comunidade por enquanto?

Não acredito que isso seja algo que possa ser [perfeitamente] corrigido de fora. A maioria das soluções userland são invólucros como este e este . Sem modificar o React, não consigo pensar em uma alternativa razoável para essa abordagem, e a embalagem definitivamente não é a solução ideal. 😕

Observe que em vuejs / vue # 7582, Vue escolheu usar um sigilo e escolheram "." como um prefixo (não o "@" do Glimmer).

Não tenho um link para uma declaração oficial sobre isso (e talvez alguém mais próximo do Vue possa confirmar explicitamente), mas parece que o Vue mudou para a Opção 2 a partir do Vue 3.0, o que significa que se comporta de forma semelhante ao Preact. (Veja este problema para o contexto.)

Por ser baseado em ref, as propriedades não são definidas no servidor. É por isso que chamo a preocupação de vaga. Não há como definir propriedades no React, então está tudo quebrado agora. A solução alternativa disponível já funciona apenas no cliente. Se houver uma maneira integrada de definir propriedades no cliente e não no servidor, ela não corrige ou interrompe o SSR repentinamente.

Você pode responder às perguntas específicas em https://github.com/facebook/react/issues/11347#issuecomment -713514984? Eu sinto como se estivéssemos conversando. Eu só quero entender todo o comportamento pretendido - de uma forma específica. O que é definido quando e o que é serializado em cada caso.

Como um desenvolvedor aleatório de Preact + WC que tropeçou aqui, eu queria salientar que a "Opção 2" não é apenas uma _alteração radical_, mas também uma _solução incompleta_ (Preact ainda pode interagir declarativamente com um subconjunto de componentes da Web válidos).

Mesmo como um usuário atual da heurística, isso parece dificilmente desejável a longo prazo.


A heurística de Preact ("opção 2") é incapaz de definir propriedades que tenham significado especial em React (chave, filhos, etc.) ou Preact (qualquer coisa começando com on ).

Ou seja, após renderizar abaixo de JSX:

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

as propriedades key , ongoing e children serão todas undefined (ou qualquer que seja o valor padrão) na instância criada de web-component .

Além disso, ao contrário do OP, esta opção também é uma alteração importante. A maioria dos componentes da web (por exemplo, feitos com lit-element ) oferecem a capacidade de passar dados ricos serializados por meio de atributos, para fins de uso com HTML puro. Esses componentes podem ser renderizados a partir do React assim

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

que será interrompida se a "opção 2" for escolhida. Não tenho certeza de quão comum é esse uso de componentes da web no React, mas definitivamente existem algumas bases de código que fazem isso, é menos eficiente que refs, mas também terser.

Por fim, o autor do Web Component é livre para anexar semânticas sutilmente diferentes aos atributos e propriedades do mesmo nome. Um exemplo seria um componente da Web imitando o comportamento do elemento nativo input - tratando o atributo value como valor inicial e apenas observando as alterações na propriedade value . Esses componentes não podem ser totalmente interagidos via jsx com a abordagem heurística e também podem experimentar uma quebra sutil se for introduzida.

Acho muito improvável que introduzamos a nova sintaxe JSX apenas por causa desse recurso específico.

Que tal usar namespaces? https://github.com/facebook/jsx/issues/66 propõe adicionar um namespace react para key e ref . Na mesma linha, algo como react-dom seria um namespace adequado para cunhar para identificar propriedades DOM? Usando o exemplo da opção 3 do OP, isso seria:

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

Isso não é o mais ergonômico. Apenas dom seria melhor, mas não tenho certeza se o React deseja cunhar namespaces que não começam com react . Além disso, a ergonomia pobre nisso não é o fim do mundo, se a ideia é que definir como propriedades deve ser o caso menos comum. Usar atributos é melhor, pois isso fornece paridade com SSR. Definir como propriedades é para quando os atributos são problemáticos (como passar um objeto ou para propriedades não apoiadas por atributos), e é precisamente para essas propriedades problemáticas que não podemos SSR para atributos de qualquer maneira, e precisaríamos hidratá-los via JS.

uma _solução incompleta_

(Preact ainda só pode interagir declarativamente com um subconjunto de componentes da Web válidos)

@brainlessbadger você também pode editar seu comentário acima para incluir o que esse subconjunto realmente é? Por exemplo. quais componentes da Web / elementos personalizados são afetados (com exemplos)? E como exatamente eles são afetados?

@karlhorky eu adicionei uma explicação. Desculpe por ser desnecessariamente vago.

Com relação aos eventos do Web Components. Ainda existe algum consenso sobre como evitar bloqueios futuros por colisão de nomes de eventos?

Este é um problema para atributos e propriedades também, pois novos atributos podem ser adicionados ao HTMLElement como todos estes: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes Parece que há alguns os recém-adicionados também, então ainda há novos sendo adicionados.

Acho que, estritamente falando, você deveria ter que usar - no nome de qualquer atributo personalizado ou evento personalizado.

Qualquer outra coisa é um compromisso que pesa os riscos desse componente bloquear uma especificação futura de usar um bom nome.

Os eventos me deixam ainda mais nervoso, porque não é apenas um risco desse componente ter um comportamento incompatível. Se o novo evento borbulhar, ele acionará todos os componentes da Web existentes naquele caminho de árvore.

Se atributos e eventos personalizados fossem obrigados a usar - em seus nomes, isso poderia ser usado como heurística para determinar o que usar. Em seguida, teríamos um caso especial dos embutidos. Já é assim que sabemos se é um elemento personalizado em primeiro lugar.

As propriedades podem então ser usadas como conveniências para fornecer uma mão curta mais agradável. Pelo menos eles podem obscurecer as propriedades integradas e não colidir com propriedades futuras.

Acho que, estritamente falando, você deve usar um - no nome de qualquer atributo personalizado ou evento personalizado.

A especificação do elemento personalizado não inclui tais restrições, portanto, aplicá-las no React afetará significativamente a interoperabilidade.

Não se deve esperar que os autores de elementos personalizados assinem os requisitos arbitrários de uma estrutura. Imagine cada estrutura fazendo isso com opiniões e convenções variadas. Isso anula o propósito de usar componentes da web. 😕

Idealmente, a estrutura deve se adaptar para acomodar o padrão.

De fato. setAttribute() e dispatchEvent() são apenas APIs não específicas de componentes da web que permitiram nomes arbitrários desde seu início. Qualquer elemento pode ter qualquer atributo e disparar qualquer evento - é apenas uma verdade básica do DOM.

O namespace data- foi adicionado por um motivo. Há uma diferença entre o que você poderia fazer tecnicamente e o que é a melhor prática. Você também pode corrigir os protótipos tecnicamente, mas obterá situações de contém / inclui.

Eu não sugeriria que apenas o React adicionasse isso, mas que a comunidade mudasse para esta convenção para ajudar a proteger o futuro namespace. Só acho lamentável que esse problema não seja levado a sério. Mas talvez a web esteja condenada a apenas adicionar nomes estranhos às novas APIs.

Eu gosto da ideia de apenas excluir os não embutidos do SSR e prefiro as propriedades durante a hidratação porque com o DOM de sombra declarativo o conteúdo real pode ser expandido dentro.

Porém, concretamente, acredito que isso causará problemas para as abordagens atuais que as pessoas usam com SSR + AMP, pois isso usa atributos e você pode assumir que o tempo de execução de AMP já foi carregado.

Supondo que a porta não esteja fechada na opção 3, ela tem uma vantagem significativa sobre a opção 2, não listada nos contras acima - O problema que encontrei com a opção 2 é que, se as bibliotecas de componentes da web forem carregadas de forma assíncrona , a pré-ação pode não saiba, para um elemento desconhecido, que uma "propriedade" será uma propriedade. Como não encontra a propriedade existente no elemento desconhecido, o padrão é um atributo. Uma solução alternativa é não renderizar esse componente até que as bibliotecas de componentes da web dependentes sejam carregadas, o que não é muito elegante (ou em termos de desempenho ideal). Como meu grupo usa asp.net, não node.js, nunca explorei o SSR de verdade, portanto, as observações abaixo são especulativas.

É um gesto simpático, eu acho, da parte da equipe do React, sugerir ir além do que outros frameworks suportam (pelo meu conhecimento), no que diz respeito a SSR, permitindo que as propriedades do objeto sejam transmitidas durante a renderização inicial, o que poderia servir ao usuário melhor.

Eu não sei se eu estou dizendo o óbvio ou não, mas acho que um pouco de um consenso com os componentes da web que para algumas propriedades, é conveniente, por vezes, definir o valor inicial do atributo através de uma string JSON , envolto em aspas simples.

No entanto, o AMP usa outra convenção - usa script type = application.json para fazer isso, que é uma alternativa razoável (mas prolixa).

Mas mantendo a abordagem de atributos:

A biblioteca de componentes da web pode então analisar o atributo ou ser passada no valor por meio de uma propriedade.

Portanto, para evitar atrasos na hidratação, seria conveniente definir:

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

durante SSR. Então, o github-icon poderia fazer seu trabalho imediatamente, fazer internamente um JSON.parse do atributo e não ter que esperar que o React passasse o valor do objeto (mais completo?).

Portanto, agora temos uma situação complicada - se renderizar no servidor, queremos que o "ícone" seja tratado como um atributo, mas ser capaz de passar o valor como um objeto, o que idealmente o mecanismo SSR seria transformado no atributo. No cliente, queremos ser capazes de passar o objeto diretamente, e não passar pela sobrecarga de stringificar e analisar.

Claro, algumas propriedades são objetos que não são passíveis de restringir / analisar, então isso não se aplica, e essas propriedades precisariam aguardar a conclusão da hidratação.

Se todas as propriedades e atributos seguiram a convenção de nomenclatura preferida da equipe React (talvez) - todos os atributos tinham travessões e todas as propriedades eram nomes compostos usando camelCase, talvez a existência de um travessão pudesse ajudar a distinguir entre atributos e propriedades:

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

O problema é que nada na sintaxe acima indica o que fazer no servidor e não no cliente e, idealmente, seríamos capazes de usar um vínculo para ambos, e o React seria inteligente o suficiente para descobrir qual usar.

O React poderia simplesmente assumir que, se uma chave de atributo / propriedade for camel case, sempre passá-la como um objeto no lado do cliente e uma serialização JSON string do objeto se for definido durante o SSR. Da mesma forma, travessões na chave podem (com segurança, eu acho) assumir que é um atributo e apenas passar o .toString () do valor. Mas acho que isso pressupõe demais. E não oferecer suporte a atributos e propriedades de palavra única, que é considerado HTML válido se os atributos forem aplicados a um componente da web que estende HTMLElement, seria muito restrito. Eu sou a favor do W3C publicar uma lista de nomes de atributos "reservados" que ele pode usar no futuro, semelhante a palavras-chave reservadas para JS e estruturas encontrando maneiras de alertar os desenvolvedores se eles estão usando indevidamente um nome de atributo reservado.

Mas acho que a opção 3 é a melhor abordagem. No entanto, se ele puder ser aprimorado, como sugerido por @gaearon , para uma experiência do usuário ainda melhor, isso seria ótimo.

Minha sugestão seria:

<github-icon icon={myDynamicIcon}/>

significa atributo (toString ()).

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

significaria - durante o SSR, ignorar, mas vincular o cliente à propriedade do objeto (após a hidratação).

E quanto ao cenário (alguns dos?) A equipe do React está interessada em resolver? Meu primeiro pensamento foi apenas algum outro sigilo, como dois pontos:

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

significaria, durante o SSR, JSON.stringify a propriedade, definida como um atributo no HTML renderizado, e passada como um objeto no cliente quando a propriedade a que está vinculado muda após a hidratação.

Isso deixa a situação complicada do que fazer com os nomes compostos. Ou seja, se definirmos:

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

não era controverso no passado que o atributo correspondente para o nome da propriedade iconProps deveria ser icon-props.

Talvez isso tenha se tornado mais polêmico, devido ao espectro de que alguns desses componentes da web poderiam, sem alterações, ser integrados à plataforma, onde os atributos não podem ter travessões (mas as propriedades podem ser camelCase). Pelo que sei, ainda não existe um elemento nativo que permita a passagem de objetos complexos por meio da desserialização JSON, mas não ficaria surpreso se a necessidade surgisse no futuro. Então, como o React saberia quando inserir travessões ou não?

A única sugestão (feia?) Que posso fazer é:

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

o que significa que, no servidor, use o atributo icon-props após a serialização, e no cliente use a propriedade iconProps, passando os objetos diretamente.

Outro (longo tiro?) Benefício potencial da notação mais detalhada, permitindo um par híbrido de atributo / propriedade, é este: Pode ser um pouco mais rápido definir repetidamente as propriedades de um elemento nativo, em vez do atributo correspondente, especialmente para propriedades que não são strings. Em caso afirmativo, o React atualmente usa atributos no servidor e propriedades no cliente? Se não, é por causa do mesmo problema de nomear as dificuldades de tradução, que essa notação resolveria?

@bahrus acho que pode ser simplificado

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

Acho que seria melhor ilustrar a questão com um exemplo concreto.

O elemento HTML Form possui vários atributos. Aqueles com "nomes compostos" nem todos parecem seguir um padrão de nomenclatura consistente - geralmente fica com todas as letras minúsculas, sem separadores, mas às vezes há um separador de traço - "aceitar-conjunto de caracteres" por exemplo, às vezes não - - "novalidate".

O nome da propriedade JS correspondente também não se encaixa em um bom padrão universal - acceptCharset, noValidate. O atributo noValidate property / novalidate é um booleano, portanto, no cliente, seria um desperdício (imagino) fazer myForm.setAttribute ('novalidate', '') ao invés de myForm.noValidate = true.

No entanto, no servidor, não faz sentido definir myForm.noValidate = true, pois queremos enviar uma representação de string do DOM, portanto, precisamos usar o atributo:

<form novalidate={shouldNotValidate}>

Na verdade, não está claro para mim que o React / JSX tenha uma maneira universal de definir um atributo booleano condicionalmente , dependendo, talvez, de uma tabela de pesquisa estática fixa, que parece (?) Excessivamente rígida . Se me permite ser tão ousado, esta parece ser outra área que o React / JSX poderia melhorar, para ser totalmente compatível com a forma (bagunçada) de funcionamento do DOM, como parte deste RFC?

Como podemos representar todos esses cenários, para que o desenvolvedor tenha total controle sobre o servidor (via atributos) e cliente (via propriedades), sem sacrificar a performance? Neste caso de um booleano como novalidate, eu proporia:

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

Se o React suportasse totalmente o DOM, por mais confuso que fosse, da maneira mais eficiente possível, acho que seria uma grande ajuda para oferecer suporte a elementos personalizados, que provavelmente também não são muito consistentes em padrões de nomenclatura.

Adicionando suporte para opcionalmente serializar propriedades de objeto para atributos via JSON.stringify no servidor, seria uma grande vantagem adicional para React e componentes da web, me parece.

Ou eu estou esquecendo de alguma coisa? ( @eavichay , como você sugere que esses cenários sejam melhor representados, não seguindo seu exemplo).

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

Questões relacionadas

UnbearableBear picture UnbearableBear  ·  3Comentários

jvorcak picture jvorcak  ·  3Comentários

krave1986 picture krave1986  ·  3Comentários

zpao picture zpao  ·  3Comentários

hnordt picture hnordt  ·  3Comentários