— Resumo: há muito código a seguir —
Na Wearable Intelligence, precisamos de uma ligação em tempo real fiável para os trabalhadores no terreno. Acima de tudo, precisamos de apresentar com precisão o estado de desconexão nos clientes web e móveis.

Não há nada de revolucionário no banner de reconexão ao estilo do Gmail, mas a forma como o gerimos utilizando o React e o NuclearJS (a implementação do Flux que usamos e adoramos) é bastante interessante, na minha opinião, e, após algumas iterações, conseguimos resolver as peculiaridades dos WebSockets nativos de forma bastante elegante. Espero que haja aqui algumas informações úteis para quem esteja a trabalhar em áreas semelhantes.
Pontos de referência
- O React é utilizado apenas para a interface do utilizador. Não há lógica de negócio — limitam-se a ligar-se aos getters do Nuclear e a chamar as ações do Nuclear.
- Os getters do Nuclear são funções puras que extraem dados dos stores, e os stores do Nuclear apenas atualizam o estado com base nas ações do Nuclear. Nem os getters nem os stores do Nuclear realizam, por si próprios, quaisquer ações externas.
- Toda a lógica do WebSocket está isolada de forma clara num serviço totalmente externo ao Nuclear.
- As ações nucleares ligam tudo, gerindo a ligação e respondendo aos eventos de manutenção do utilizador e do socket.
React: Só a interface do utilizador, senhora
Utilizamos um servidor de «Signaling» para transmitir eventos em tempo real a clientes web e móveis. Para integrar esta funcionalidade em qualquer uma das nossas aplicações web, basta incluir um componente «Signaling» em qualquer componente pai «Authenticated».
O Nuclear permite-nos armazenar o estado da ligação de sinalização como um mapa imutável. Podemos disparar uma ação de ligação quando o componente é montado e quando o utilizador a solicita explicitamente, e podemos desligar-nos quando o componente é desmontado.
O resto é a interface do utilizador. É extremamente simples para os nossos designers perceberem como tudo funciona, sem terem de se preocupar com toda a maquinaria que está por trás, e podem editar tranquilamente o código HTML ou o SCSS relacionado. E pronto!
O Provedor WebSocket: Como gerir cuidadosamente os singletons
No passado, a minha experiência com ligações em tempo real era muito mais simples graças à utilização do Socket.io, mas, neste caso, precisamos de criar e gerir WebSockets de forma nativa para nos ligarmos a um servidor já existente (talvez um tema para outro artigo).
A parte complicada é que vamos precisar de ações do Nuclear para fazer referência à instância atual do socket; por isso, precisamos de um singleton que possamos persistir e alterar, algo que é melhor manter totalmente fora do Nuclear.
Este fornecedor pode ser chamado com segurança a partir de qualquer ação e é fiável para fornecer a instância atual. Conforme solicitado, a instância pode ser reiniciada de forma forçada, o que é uma necessidade infeliz durante o processo de reconexão.
Como bónus, o fornecedor também mantém um mapa permanente dos temporizadores, evitando que as lojas Nuclear tenham de definir e anular intervalos.
A nossa implementação específica do socket exige que enviemos um pacote de presença ao servidor a cada ~30 segundos; por isso, o provedor tem essa funcionalidade integrada e mantém um mapa que associa os tipos de mensagem aos respetivos manipuladores (cada um dos quais invoca ações do Nuclear a partir de outros módulos).
Ações nucleares: os pilares
Para manter tudo organizado e claro, pretendemos apenas disponibilizar as ações «ligar» e «desligar». Todo o resto é um pormenor de implementação interna.
«Connect» é o único método que desencadeia uma reinicialização total da ligação ao passar «true» como terceiro argumento. Outras instanciações do WebSocketProvider limitam-se a devolver a instância existente.
É nas restantes funções internas que a verdadeira ação se desenrola. Os manipuladores de eventos também iniciam e param os temporizadores adequados.
Se a ligação estiver no estado «pronta», o temporizador de identificação envia um ping a cada 30 segundos, cumprindo assim o nosso requisito de presença para o próprio servidor de sinalização.
Se a ligação for encerrada ou ficar «inativa» (sem evento de encerramento, mas sem resposta aos pings durante mais de um minuto), o temporizador de identificação é interrompido e o temporizador de reconexão é iniciado. A cada segundo, é disparada a ação UPDATE_RECONNECT_TIMER, que a nossa loja utiliza para atualizar o seu estado, de modo a que o nosso componente React não precise de se ocupar disso.
Uma vez que o WebSocketProvider verifica se existe um temporizador antes de definir o intervalo, não há risco de funções como a checkConnection serem chamadas demasiadas vezes.
A depuração do NuclearJS é um sonho tornado realidade
Quando foi a última vez que ouviste isso? Eis o que vejo quando estou a depurar uma nova alteração:

Qualquer um desses objetos de registo pode ser expandido para apresentar o conteúdo da ação, bem como o estado da aplicação naquele momento. Já não é preciso espalhar console.log por todo o lado para determinar o que aconteceu entre os eventos.
Neste caso, ao iniciar sessão, é possível ver que a aplicação inicia uma tentativa de ligação, comunica a alteração de estado quando a ligação é estabelecida, envia um ping de presença e recebe o resultado. Um par de pings de identificação e de resposta chega 30 segundos depois.
Depois, desliguei manualmente o servidor de sinalização (localmente!) e é possível ver o evento de encerramento a ocorrer, seguido de uma tentativa imediata e mal sucedida de restabelecimento da ligação. O temporizador de restabelecimento atualiza-se a cada segundo, mas assim que volto a ativar a sinalização, recebemos os eventos de ligação esperados.
Ao terminar a sessão, tudo é desligado e limpo, e vemos a nossa página de início de sessão a ser apresentada.
Se achaste isto útil, não hesites em recomendá-lo.
Ah, sim, e estamos a recrutar o nosso terceiro Engenheiro Front-End, caso te apeteça ser pago para dar o teu melhor com React, Nuclear e ImmutableJS numa startup financiada na Série A, apoiada pela Andreessen Horowitz, First Round, Kleiner Perkins e Google Ventures, entre outros.











