Trabalhando com eventos no JavaScript

Matheus Castiglioni
Training Center
Published in
6 min readAug 30, 2018

--

No exemplo do post 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...).

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.

Obs: No código acima, nem todos os componentes 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 ficar facilitar o entendimento e visualização da arvore final de componentes, se quiser, abaixo listo o template individualmente:

App:

<App> 
<ComponenteA/>
<ComponenteB/>
<ComponenteC/>
<ComponenteD/>
</App>

ComponenteA:

<ComponenteA>
<ComponenteFilhoA/>
<ComponenteFilhoB/>
<ComponenteFilhoC/>
</ComponenteA>

ComponenteFilhoA:

<ComponenteFilhoA>
<OutroFilho>
</ComponenteFilhoA>

ComponenteB:

<ComponenteB>
<ComponenteFilhoB/>
</ComponenteB>

Repare que cada um deles possuem seus respectivos filhos, onde, na renderização final, vão chegar na árvore informada acima.

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 a eventos). Tudo isso é possível através de publicações (publish) e inscrições (subscribe).

A ideia do nosso cenário é: Quando a nova funcionalidade do componente OutroFilho for executada, será disparado um evento para a aplicação e quem (componente que se inscreveu para receber o evento) estiver ouvindo esse evento, poderá reagir á ele, ou seja, o componente que.

Instalando a biblioteca

Podemos realizar a instalação da PubSub de várias maneiras diferentes, sendo elas:

  1. Instalando via gerenciador de pacotes:

Ela pode ser instalada tanto com npm ou yarn:

npm i pubsub-js

ou

yarn add pubsub-js 

2. Também podemos utilizá-la diretamente através de suas CDN’s:

3. Realizar o download de sua versão “taggeada” através do Github: tagged versions

4. Antigamente também era possivel 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, vamos publicar um evento chamado NOVA_FUNCIONALIDADE, dentro da nossa função:

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 repassamos ele para quem estiver ouvindo o evento.

Podemos fazer isso da seguinte maneira:

novaFuncionalidade = () => { 
const cliente = buscarCliente()
PubSub.publish("NOVA_FUNCIONALIDADE", cliente)
}

Agora, quem ouvir nosso evento conseguirá ter acesso ao cliente encontrado.

Entendendo a função publish

A função publish pode receber dois parâmetros:

  1. O primeiro é o nosso evento (também chamado de tópico) que estamos publicando, em outras palavras, o nome do evento (esse nome deve ser guardado para depois ser ouvido).
  2. 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 irá travar enquanto o evento é emitido e ouvido. A PubSub não fica acoplada (presa) em sua sincronização, 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 à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.subscribe("NOVA_FUNCIONALIDADE", this.receberCliente)
}

Repare que para ouvir/receber/subescrever em um evento, devemos utilizar a função subscribe, a mesma recebe dois parâmetros:

  1. O nome do evento/tópico que estamos querendo ouvir.
  2. 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.subscribe("NOVA_FUNCIONALIDADE", (topico, cliente) => this.receberCliente(cliente))
}

Obs: 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 ira 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 terão suas funções de callback executadas.

Cancelando um recebimento

Até agora vimos como emitir/receber eventos, mas, também é possível cancelar o recebimento.

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.subscribe("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 sua aplicação é ou tem chances de se tornar uma aplicação de múltiplos processos (multi process), então eles recomendam que você faça uso de bibliotecas como redis PubSub.

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.

Espero que tenha gostado, até a próxima.

Publicado originalmente em blog.matheuscastiglioni.com.br em 30 de Agosto de 2018.

--

--

Matheus Castiglioni
Training Center

Apaixonado pelo mundo dos códigos e um eterno estudante, gosto de aprender e saber um pouco de tudo, aquela curiosidade de saber como tudo funciona.