Compartilhando comportamento no React
Reutilização de código é um dos principais pilares do desenvolvimento de software. É o conceito por trás de ideias básicas como funções, orientação a objetos e muitas outras.
No mundo do React, inicialmente, a forma idiomática de compartilhar comportamento entre componentes era através de mixins.
Suponha o seguinte componente que simplesmente conta o número de vezes que o usuário clica em um botão.
Agora, digamos que você queira utilizar a mesma lógica em outro componente. Contar quantas vezes o usuário faz hover em uma imagem, por exemplo.
Voce teria que adicionar o state count e repetir a função que incrementa o contador nesse novo HoverCounter.
Mixins
Para evitar essa repetição, o time do React criou os mixins. Com eles, era possível extrair esse comportamento comum e reutilizá-lo.
Essa técnica resolvia a questão do compartilhamento de código, mas tinha vários problemas.
O mais óbvio (e de certa forma mais urgente), era o fato de que os mixins eram incompatíveis com a nova sintaxe de classes que foi introduzida no ES6, e que vinha se popularizando na época.
Mas haviam outros problemas mais sutis, como a possibilidade de colisão de nomes: imagine que o App do exemplo acima adicione outro mixin que tenha um state chamado count. Isso poderia causar problemas difíceis de debugar.
Porém, para mim, o pior problema era o da indireção. Não fica claro, para quem olha para o componente App, da onde esta vindo this.state.count e this.handleClick. São valores mágicos que simplesmente aparecem no seu componente.
Lembre-se de que o código do mixin podia estar em outra parte do code base, ou, até mesmo, ser uma dependência externa. Para piorar, um componente podia ter diversos mixins. Para saber o que vinha cada um desses valores mágicos ficava complicado.
Numa tentativa de resolver, principalmente, o problema da incompatibilidade com a sintaxe de classes, a comunidade React concebeu o famigerado…
Higher Order Component
Ou ‘Componente de alta ordem’.
A ideia dessa técnica é extrair a lógica a ser compartilhada para um outro componente que usa o seu componente internamente.
No nosso exemplo do contador, o primeiro passo seria mudar nosso App para receber o que vinha do mixin, como uma prop.
A lógica do contador em si fica em outro componente:
A idéia básica é essa.
No código acima a gente ‘marretou’ o App no render do Counter para ficar mais fácil de entender, mas, obviamente a gente não conseguiria reutilizar o Counter desse jeito. A gente tem que achar uma forma do Counter receber o App como 'parâmetro'.
E esse é o pulo do gato dos ‘HOC’. Criar uma função que recebe o seu componente como parâmetro (no caso, o App) e retorna uma versão do Counter que utiliza esse parâmetro no render. Dessa forma a gente pode passar outros componentes para essa função, e usar a lógica do contador em vários lugares. Parece complicado, né? Olhando o código fica mais fácil de entender.
Como voce pode ver, essa função cria e retorna um novo componente. Esse novo componente vai renderizar o componente que foi passado como parâmetro.
Para gerar uma versão do seu componente com o contador:
Pronto, agora o App tem um contador, e qualquer outro componente também pode ter.
Esse técnica resolve alguns dos problemas que a gente tinha com os mixins, o principal deles é que ela funciona com classes.
Porem, ele introduz alguns outros problemas, como encadeação de HOCs. Imagina que a gente queira usar um outro HOC, que adiciona a prop xpto no nosso componente, por exemplo. Se a gente fizer:
O componente AppWithCounterAndXPTO não teria a prop xpto, porque no render do nosso HOC a gente não passa essa prop para o App.
A gente teria que mudar o render do nosso HOC para retornar:
Dessa forma a gente passaria adiante qualquer prop que pudessemos receber de outros HOCs. E isso deveria ser feito em todos os HOCs.
Mas o pior de tudo, na minha opinião, é que HOCs não resolvem o problemas da indireção.
Render Props
Uma das soluções propostas pare resolver esses problemas é a técnica chamada de render props. O que muda, nessa técnica, em relação aos HOCs, é que em vez de renderizar o componente que veio como ‘parâmetro’ a gente chama uma função passando o que a gente quer compartilhar e usa o retorno dessa função no render.
De novo, é mais fácil olhando o código:
A única coisa que muda no Counter (agora chamado WithCounter) é o render, onde a gente executa a função que será passada como children e passa count e handleClick como parâmetros.
O App fica assim:
Não muda muita coisa, mas agora fica perfeitamente claro da onde count e handleClick estão vindo!
A gente ainda evita os problemas de colisão de nomes, já que podemos renomear os parâmetros para o que a gente quiser.
