Imagine que temos um projeto em andamento, porém, precisamos adicionar uma nova funcionalidade em um de nossos componentes. Hoje temos nossa árvore de componentes da seguinte maneira:
<App> <ComponenteA> <ComponenteFilhoA> <OutroFilho> </ComponenteFilhoA> <ComponenteFilhoB/> <ComponenteFilhoC/> </ComponenteA> <ComponenteB> <ComponenteFilhoB/> </ComponenteB> <ComponenteC/> <ComponenteD/> </App>
Certo, dado nossa árvore atual, sabemos que o responsável por essa nova funcionalidade será o componente OutroFilho
que é filho de ComponenteFilhoA
e por sua vez é filho de ComponenteA
. Além de todos esses relacionamentos entre pais e filhos, também temos os irmãos, sobrinhos, primos, tios, e por aí vai.
A nova funcionalidade criada em OutroFilho
deve impactar e atualizar os componentes ComponenteFilhoB
, ComponenteC
e ComponenteD
. Como podemos resolver esse problema?
Uma das maneiras de resolver o problema seria nosso componente App
cuidar de tudo; dessa maneira, poderíamos passar via props
uma função contendo a nova funcionalidade para o componente ComponenteA, que por sua vez repassaria essa props
para seu filho OutroFilho
. Assim, quando a função for executada, atualizaríamos o state do componente ComponenteA
, que por sua vez seria passado para os demais componentes que precisam daqueles dados.
Observação: No código acima, todos os componentes não estão diretamente no componente App
. O mesmo apenas tem em sua declaração os componentes: ComponenteA
, ComponenteB
, ComponenteC
e ComponenteD
. Os demais componentes são filhos de filhos do componente App
. Apenas deixei tudo junto para facilitar o entendimento e visualização da arvore final de componentes.
Beleza, será que não existe uma maneira mais elegante de resolver o problema? Sim. Para isso temos a biblioteca chamada PubSubJS.
Conhecendo a PubSubJS
A PubSub é uma biblioteca muito pequena, em sua versão minificada e “gzipada” podemos contar com menos de 1kb
, responsável por trabalhar com eventos. Conseguimos realizar a prática do event driven programming (Programação orientada á eventos). Tudo isso é possível através de publicações (publish) e inscrições (subscribe).
A ideia do nosso cenário, é: o componente OutroFilho
terá sua nova funcionalidade quando ela for executada. Será disparado um evento para a aplicação, e quem estiver ouvindo esse evento poderá reagir a ele.
Instalando a biblioteca
Podemos realizar a instalação da PubSub de várias maneiras diferentes, sendo elas:
Instalando via gerenciador de pacotes:
Ela pode ser instalada tanto com npm
ou yarn
:
npm i pubsub-js
Ou:
yarn add pubsub-js
Também podemos utilizá-la diretamente através de suas CDN’s:
Realizar o download de sua versão “taggeada” através do Github: tagged versions.
Antigamente também era possível instalá-la via bower
, porém, a última versão disponível através do mesmo é: 1.5.4
, versão um pouco desatualizada comparada com a atual: 1.6.1
.
Publicando um evento
Voltando a nossa funcionalidade, como podemos publicar um evento? O primeiro passo é importar a biblioteca dentro de onde o evento será publicado, ou seja, dentro do nosso componente OutroFilho
:
import PubSub from "pubsub-js"
Agora que já temos a PubSub
importada e disponível para uso, dentro da nossa função vamos publicar um evento:
novaFuncionalidade = () => { PubSub.publish("NOVA_FUNCIONALIDADE") }
Para realizar a publicação do evento, utilizamos a função publish
.
No exemplo acima, apenas foi publicado um evento, sem mandar informações para o mesmo. Porém, nossa funcionalidade, exige que busquemos um determinado cliente e repassemos ele para quem ouvir o evento. Podemos fazer isso da seguinte maneira:
novaFuncionalidade = () => { const cliente = buscarCiente() PubSub.publish("NOVA_FUNCIONALIDADE", cliente) }
Agora, quem ouvir nosso evento conseguirá ter acesso ao cliente encontrado.
Observação: Repare que conseguimos emitir nosso evento através de uma publicação, portanto, não fique confuso, tanto publicar quanto emitir, basicamente são as mesmas coisas dentro desse cenário.
Entendendo a função publish
A função publish
pode receber dois parâmetros:
- O primeiro é o nosso evento/tópico que estamos publicando. Em outras palavras, o nome do evento (esse nome deve ser guardado para depois ser ouvido).
- O segundo é alguma informação que precisamos passar para quem ouvir nosso evento.
Tópicos/eventos são publicados de forma assíncrona. Dessa maneira, nossa aplicação não travará enquanto o evento é emitido e ouvido. A PubSub não fica acoplada (presa) em sua sincronização, e assim ela consegue ajudar a manter a nossa aplicação rodando de forma que os tópicos/eventos não irão bloquear o usuário.
Se você preferir ou precisar, também é possível publicar/emitir eventos de forma síncrona através da função publishSync
. Seu funcionamento é semelhante a publish
apenas mudando sua forma de assíncrona para síncrona.
Recebendo evento
Legal, conseguimos publicar nosso evento. Agora precisamos de alguma maneira, dentro dos componentes ComponenteFilhoB
, ComponenteC e ComponenteD
, ouvir e receber o evento quando o mesmo for publicado/emitido. Podemos fazer isso através da função subscribe
, da seguinte maneira:
import PubSub from "pubsub-js" componentDidMount = () => { PubSub.subcribe("NOVA_FUNCIONALIDADE", this.receberCliente) }
Repare que para ouvir/receber/subrescrever em um evento, devemos utilizar a função subscribe
; a mesma recebe dois parâmetros:
- O nome do evento/tópico que estamos querendo ouvir
- Uma função de callback que deverá ser executada quando o evento for emitido
Aí eu te pergunto: precisamos receber os dados do cliente que foram passados durante a emissão do evento; como podemos fazer isso? No segundo parâmetro da função subscribe
, devemos retornar uma função de callback, porém, essa função pode receber dois parâmetros, sendo eles: o nome do tópico/evento que foi publicado e os dados que foram passados:
componentDidMount = () => { PubSub.subcribe("NOVA_FUNCIONALIDADE", (topico, cliente) => this.receberCliente(cliente)) }
Observação: o código de subscribe
deve estar em todos os componentes que pretendem ouvir e receber o evento.
Saiba mais
Quando realizamos o subscribe
dentro do ciclo de vida componentDidMount
, a função de callback não será executada naquele momento, apenas realizamos e dizemos para a PubSub que queremos ouvir aquele evento/tópico. A função de callback apenas será executada quando o evento for emitido, ou seja, enquanto não realizamos o publish
, nada será feito e chamado.
Assim que a função publish
for executada, a PubSub irá disparar esse evento de forma global para nossa aplicação, e todos os lugares que estiverem fazendo um subscribe
para o mesmo nome do evento/tópico emitido será executada a função de callback referente a eles.
Cancelando um recebimento
Até agora vimos como emitir/receber eventos, mas também é possível cancelar esse o recebimento.
componentDidMount = () => { const id = PubSub.subcribe("NOVA_FUNCIONALIDADE", (topico, cliente) => this.receberCliente(cliente)) }
Quando realizarmos o subscribe
, a função devolve um id para identificar aquela inscrição. Em caso de necessidade podemos cancelar o subscribe
através da função unsubscribe
:
componentDidMount = () => { const id = PubSub.subcribe("NOVA_FUNCIONALIDADE", (topico, cliente) => this.receberCliente(cliente)) PubSub.unsubscribe(id) }
Como você pode ver, a função unsubscribe
recebe o id que pretendemos cancelar. Se surgir a necessidade de voltar a ouvir o evento, simplesmente realize o subscribe
novamente.
Saiba mais
Também é possível cancelar todos as incrições de uma única vez, através da função clearAllSubscriptions
:
PubSub.clearAllSubscriptions()
Dessa maneira nenhum evento mais será ouvido até que seja realizado o subscribe
novamente.
Dica para ouvir/receber eventos
Em vez de ficar passando a mesma String
tanto para emitir quanto para receber, recomenda-se que exista um arquivo de constant em nosso projeto:
const Eventos = { NOVA_FUNCIONALIDADE: "NOVA_FUNCIONALIDADE" } export { Eventos }
Dessa maneira, podemos realizar o publish
e subscribe
da seguinte maneira:
import { Eventos } from "constants/eventos.js" PubSub.publish(Eventos.NOVA_FUNCIONALIDADE) PubSub.subscribe(Eventos.NOVA_FUNCIONALIDADE)
Com isso, se um dia o nome de nosso evento precisar mudar, não precisaremos sair procurando onde o mesmo está sendo utilizado; basta modificar nossa constante de eventos.
Saiba mais
A biblioteca PubSub é utilizada e voltada para aplicações de processo único (Single process). Se a sua aplicação é ou tem chances de se tornar uma aplicação de multíplos processos (multi process), então eles recomendam que você faça uso de bibliotecas como redis PubSub.
No exemplo do artigo, utilizei a PubSub dentro de uma aplicação React
, porém, é possível utilizá-la em qualquer lugar que pode executar JavaScript (Browser, NodeJS, Angular, Vue, Elm, Ember, Meteor, etc).
Conclusão
Para esse post é isso, com a PubSub conseguimos simplificar e melhorar muito nossos códigos através de emissões e inscrições de eventos, muitas bibliotecas fazem o isso desse padrão.
E aí, você já conhecia a PubSub? Não deixe de comentar, se gostou desse e outros post’s se inscreva na newsletter para receber novidades por email.
Espero que tenha gostado, e até a próxima!