Escrevendo uma API para componentes React, parte 3: a ordem dos adereços é importante

Escrevendo uma API para componentes React, parte 1: não crie objetos conflitantes

Escrevendo uma API para componentes de reação, parte 2: dê nomes ao comportamento, não à interação

Escrevendo uma API para componentes React, parte 3: a ordem dos adereços é importante

Escrevendo uma API para reagir componentes, parte 4: Cuidado com o Apropacalypse!

Escrevendo uma API para reagir componentes, parte 5: apenas usar composição

Escrevemos API para componentes React, parte 6: criamos comunicação entre componentes

Vamos começar com um componente React simples que exibe uma tag de âncora:


link


<Link href="sid.studio">Click me</Link> //   : <a href="sid.studio" class="link">Click me</a> 

Aqui está a aparência do código do componente:


 const Link = props => { return ( <a href={props.href} className="link"> {props.children} </a> ) } 

Também queremos adicionar atributos html ao elemento, como id , target , title , atributo de data-attr , etc.


Como existem muitos atributos HTML, podemos passar todos os adereços e adicionar os que precisamos (como className )


(Nota: você não deve passar os atributos criados para este componente que não estão na especificação HTML)


Nesse caso, você pode simplesmente usar className


 const Link = props => { /*    (spread ),     ( ) */ return <a {...props} className="link" /> } 

É aí que fica interessante.


Tudo parece estar bem quando alguém passa um id ou target :


 <Link href="sid.studio" id="my-link">Click me</Link> //   : <a href="sid.studio" id="my-link" class="link">Click me</a> 

mas o que acontece quando alguém passa className ?


link


 <Link href="sid.studio" className="red-link">Click me</Link> //   : <a href="sid.studio" class="link">Click me</a> 

Bem, nada aconteceu. Reagir completamente ignorado a classe personalizada. Vamos voltar à função:


 const Link = props => { return <a {...props} className="link" /> } 

Ok, vamos imaginar como este ...props compila, o código acima é equivalente a isso:


 const Link = props => { return ( <a href="sid.studio" className="red-link" className="link" > Click me </a> ) } 

Vê o conflito? Existem dois className . Como o React lida com isso?


Bem, o React não faz nada. Babel faz!


Lembre-se de que o JSX "produz" React.createElement . Os adereços são convertidos em um objeto e passados ​​como argumento. Os objetos não oferecem suporte a chaves duplicadas; portanto, o segundo className substituirá o primeiro.


 const Link = props => { return React.createElement( 'a', { className: 'link', href: 'sid.studio' }, 'Click me' ) } 



Ok, agora que sabemos do problema, como podemos resolvê-lo?


É útil entender que esse erro ocorreu devido a um conflito de nome, e isso pode acontecer com qualquer prop, e não apenas com className . Portanto, a decisão depende do comportamento que você deseja implementar.


Existem três cenários possíveis:


  1. Um desenvolvedor que utilize nosso componente poderá substituir o valor padrão de prop.
  2. Não queremos deixar que o desenvolvedor altere alguns adereços
  3. O desenvolvedor deve poder adicionar valores enquanto mantém o valor padrão.

Vamos separá-los um de cada vez.


1. O desenvolvedor que usa nosso componente deve poder substituir o valor de prop padrão


Esse é o comportamento que você normalmente espera de outros atributos - id , title etc.


Frequentemente, vemos a configuração do test id no cosmos (o sistema de design no qual estou trabalhando). Cada componente recebe um data-test-id padrão; às vezes, os desenvolvedores desejam anexar seu próprio identificador de teste, para indicar um uso específico.


Aqui está um desses casos de uso:


farinha de rosca


 const Breadcrumb = () => ( <div className="breadcrumb" data-test-id="breadcrumb"> <Link data-test-id="breadcrumb.link">Home</Link> <Link data-test-id="breadcrumb.link">Parent</Link> <Link data-test-id="breadcrumb.link">Page</Link> </div> ) 

Breadcrumb usa o link, mas você deseja usá-lo em testes com data-test-id mais específicos. Há uma falha nele.


Na maioria dos casos, os adereços personalizados devem ter precedência sobre os adereços padrão.


Na prática, isso significa que os adereços padrão devem ir primeiro e, em seguida, {...props} para substituí-los.


 const Link = props => { return ( <a className="link" data-test-id="link" {...props} /> ) } 

Lembre-se de que a segunda aparência do data-test-id (de props) substituirá a primeira (por padrão). Portanto, quando um desenvolvedor anexa seu próprio data-test-id ou className , ele substitui o que era por padrão:


 1. <Link href="sid.studio">Click me</Link> 2. <Link href="sid.studio" data-test-id="breadcrumb.link">Click me</Link> //   : 1. <a class="link" href="sid.studio" data-test-id="link">Click me</a> 2. <a class="link" href="sid.studio" data-test-id="breadcrumb.link">Click me</a> 

Podemos fazer o mesmo com className :


link vermelho


 <Link href="sid.studio" className="red-link">Click me</Link> //   : <a href="sid.studio" class="red-link" data-test-id="link">Click me</a> 

Parece um pouco estranho, não tenho certeza se devemos permitir isso! Vamos falar mais sobre isso.


2. Não queremos permitir que o desenvolvedor altere alguns adereços


Suponha que não queremos que os desenvolvedores alterem a aparência (via className ), mas não nos importamos que eles alterem outros objetos, como id , data-test-id , etc.


Podemos fazer isso ordenando a ordem dos nossos atributos:


 const Link = props => { return ( <a data-test-id="link" {...props} className="link" /> ) } 

Lembre-se de que o atributo à direita substituirá o atributo à esquerda. Assim, tudo antes de {...props} pode ser redefinido, mas tudo depois dele não pode ser redefinido.


Para simplificar o trabalho dos desenvolvedores, você pode exibir um aviso de que não pode especificar seu className .


Eu gosto de criar meus próprios adereços de verificação de tipo para isso:


 Link.PropTypes = { className: function(props) { if (props.className) { return new Error( `  className  Link,      ` ) } } } 

Eu tenho um vídeo que fala sobre a verificação de tipos personalizados de adereços , caso você esteja interessado em como escrevê-los.


Agora, quando o desenvolvedor tentar substituir className , isso não funcionará e o desenvolvedor receberá um aviso.


link


 <Link href="sid.studio" className="red-link">Click me</Link> //   : <a href="sid.studio" class="link">Click me</a> 

 :   :   className  Link,       

Honestamente, eu tive que usar esse modelo apenas uma ou duas vezes. Geralmente você confia no desenvolvedor usando seu componente.


O que nos leva a compartilhar.


3. O desenvolvedor deve poder adicionar valores enquanto mantém o valor padrão


Este é talvez o caso de uso mais comum para classes.


link


 <Link href="sid.studio" className="underline">Click me</Link> //   : <a href="sid.studio" class="link underline">Click me</a> 

A implementação é assim:


 const Link = props => { /*  className   */ const { className, otherProps } = props /*     */ const classes = 'link ' + className return ( <a data-test-id="link" className={classes} {...otherProps} /*     */ /> ) } 

Esse modelo também é útil para aceitar manipuladores de eventos (por exemplo, onClick ) para um componente que já os possui.


mudar


 <Switch onClick={value => console.log(value)} /> 

Aqui está a aparência da implementação deste componente:


 class Switch extends React.Component { state = { enabled: false } onToggle = event => { /*      */ this.setState({ enabled: !this.state.enabled }) /*       */ if (typeof this.props.onClick === 'function') { this.props.onClick(event, this.state.enabled) } } render() { /*        ️ */ return <div class="toggler" onClick={this.onToggle} /> } } 

Existe outra maneira de evitar conflitos de nomes nos manipuladores de eventos: descrevi-o na API de gravação para componentes de reação, parte 2: dê nomes ao comportamento, não às formas de interação .




Para cada cenário, você pode usar diferentes abordagens.


  1. Na maioria das vezes: o desenvolvedor deve poder alterar o valor do suporte, cujo valor foi definido por padrão
  2. Geralmente para estilos e manipuladores de eventos: o desenvolvedor deve poder adicionar um valor acima do valor padrão
  3. Um caso raro em que você precisa limitar as ações do desenvolvedor: o desenvolvedor não tem permissão para alterar o comportamento, é necessário ignorar seus valores e, ao mesmo tempo, mostrar avisos

Source: https://habr.com/ru/post/pt459380/


All Articles