Solução 01
Veja a diff no Github
Esse exercício possui várias partes. Vamos então, parte por parte, resolvê-lo.
Extraindo um componente Button
e tipando o children
Antes de mais nada, vamos criar um componente Button
. Poderíamos fazer algo como”
Recebendo (e repassando) o onClick
Vamos receber então a função que deverá ser disparada no clique. Com isso, vamos criar a prop onClick
. Mas isso por si só não basta. Também precisamos repassá-la ao elemento html, que é o nosso <button>
(com letra minúscula!):
Substituindo os botões pelo novo componente
Vamos agora usar nosso novo botão:
Passando a prop disabled
Assim como passamos a prop onClick
, definindo ela no nosso componente, poderíamos fazer a mesma coisa com outras props que quiséssemos utilizar. Nesse caso, precisamos agora da prop disabled
.
Não faz muito sentido: se tivéssemos um simples <button>
html já teríamos todas as props que o HTML nos dá. Mas a partir do momento em que fazemos um <Button>
personalizado precisamos criar cada uma dessas props novamente? Será que não há um jeito melhor?
Sim, há.
Passando não apenas a prop disabled, mas todas os atributos de um botão
Ok, vamos então fazer isso. E para isso funcionar, vamos usar o rest
e o spread
operator. O rest
para “pegarmos” toda e qualquer prop que for passada para nosso <Button>
. O spread
para repassarmos para nosso elemento <button>
. Com isso posso até apagar a prop onClick
!
Veja como nosso componente vai ficar muito mais simples (e funcionando ainda melhor):
Mas ainda falta uma coisa. O botão funciona, podemos passar o disabled
para ele e o disabled funciona como esperado também. Mas o TS não está gostando e está apontando erro:
O que falta aqui é dizer para o typescript que todas essas props que podemos receber são props específicas do botão. E nosso componente pode aceitar quaisquer props de botão!
O tipo utilitário ComponentPropsWithoutRef
vai nos salvar 🙏
Podemos manter tudo simples e apenas utilizar o tipo utilitário ComponentPropsWithoutRef
. Esse tipo deve receber como parâmetro qual elemento html estamos querendo “emular” (nesse caso um button
). E voilá! Todos os atributos html de um botão já estão tipados em nosso componente.
Podemos usar então o disabled
, o onClick
, o type
, e centenas de outros atributos de <button>
diretamente em nosso componente <Button>
!
Maravilha! Agora temos um componente realmente flexível, simples e sem erros de tipagem!
Teste você: tente passar uma prop específica de um botão que nosso componente aceitará. Mas se tentar passar uma prop que não é de um botão, nosso componente irá reclamar!