dezembro 20, 2016 4:00 pm Publicado por major Deixe um comentário
Há uns dias, no primeiro hangout do React Cast, apareceu o assunto sobre como React e Redux nos faziam ser programadores melhores. Neste artigo, vou esclarecer os pontos que me levam a acreditar nisso.
Quando começamos a trabalhar como programadores, é muito comum aprendermos a trabalhar com abstrações que facilitam nosso caminho e nossa curva de aprendizado. Um grande exemplo disso é o jQuery, que provavelmente é a primeira lib com que os desenvolvedores Frontend tem contato.
Abstrações são ótimas, elas podem facilitar muito as nossas vidas e aumentar nossa produtividade. Porém, é muito comum programadores ficarem presos às abstrações e se esquecerem de aprender os fundamentos básicos referentes à tecnologia abstraída. Os programadores se prendem aos vícios das abstrações e ficam completamente dependentes delas.
Existe este meme clássico que exemplifica claramente isso:
Disclaimer: Antes de continuar este artigo, deixo claro que eu trabalhei mais de um ano com Angular, em grandes aplicações, antes de começar a trabalhar com React, e ainda trabalho com ele em aplicações já em produção. Falo aqui como alguém que antes defendia muito o framework e adorava trabalhar com ele.
Trabalhando com Angular e outros frameworks, nós acabamos seguindo p0r esse mesmo caminho, de pensar como resolver problema X com o framework, antes mesmo de pensar como poderíamos resolvê-lo sem ele.
Um claro exemplo que eu poderia dar seria o angular.forEach. Nós poderíamos simplesmente utilizar Array.forEach, nativo do JavaScript.
Eu sei que há casos em que você precisa iterar sobre um objeto e não um array; para isso, bastaria utilizar Object.keys(Array).forEach, também nativo. Mas com o Angular é mais cômodo, não é?
Nós não paramos por aí, utilizamos factory, service, session, value, constant, dependency Injection, tudo isso específico do Angular. Quando precisamos fazer uma integração específica, como integrar a aplicação com um serviço de websocket, utilizamos módulos como Angular Socket IO, em vez de utilizar diretamente o socket.io-client. Aos poucos, você olha para a sua aplicação e percebe que ela está totalmente acoplada e dependente do framework.
Você pode vir com o argumento que é possível deixar a aplicação menos acoplada e que esses módulos na maioria das vezes só lidam com a natureza assíncrona do Angular. Mas tentar deixar o Angular menos acoplado, utilizando funções construtoras em vez de factories e aí por diante, implica ainda ter que implementar inúmeros workarounds ao longo do código, como: $scope.$apply para sincronizar o digest cycle. Sem contar que esse não é o Angular way de trabalhar e seria considerado uma má prática.
E mesmo contornando essas dependências de inúmeras formas, no final do dia você ainda não conseguiria rodar nenhum teste unitário sem ter o Angular como dependência, pelo menos não de uma forma viável.
Eu sei, peguei bastante no pé do Angular, mas justamente por ele ser um dos frameworks mais populares, por eu ter mais experiência com ele, e porque utilizando-o como exemplo, é fácil expor os problemas que é se amarrar a um framework.
Ter um framework que cobre de ponta a ponta da sua aplicação é ruim, porque toda a aplicação tende a ser desenvolvida da maneira dele. Se amanhã surge uma lib que resolve uma parte do problema de uma maneira melhor que o seu framework, dificilmente você vai conseguir migrar somente uma responsabilidade para a lib, pelo menos não de forma fluída.
Vemos esse mesmo tipo de opinião também no Backend. No artigo O fim da era dos frameworks full stack, o Elton Minetto fala sobre o assunto.
Separation of Concerns, ou Separação de conceitos, é um termo cunhado por Edsger W. Dijkstra, em 1974.
No básico, se refere a identificar as responsabilidades que existem dentro de uma aplicação, isolá-las e delegá-las, de uma forma que fique claro o que cada parte do seu software faz e que fique claro onde deve ser feita cada coisa.
Seu software deve parecer com um brinquedo de lego, no qual você pode tirar uma peça amarela, colocar uma azul e ele continua funcionando, se limitando apenas a possuir as mesmas entradas e saídas.
Interação do usuário: Click em botão, entrada de dados em um formulário etc.
IO: Eventos de rede, como início e fim de um request.
A forma como cada uma dessas partes se integram é bem simples. O State fica responsável por armazenar os dados e entregá-los para a View. A View, por sua vez, irá se renderizar de acordo com os dados que recebe do State. As Actions, ao ocorrerem, poderão causar uma alteração no State, que entrega os novos dados para View, e assim segue esse ciclo.
Essas características podem ser vistas em qualquer SPA, independentemente da tecnologia em que seja desenvolvida. O problema é que muitos frameworks deixam essas três características totalmente acopladas, não sendo possível substituir o "responsável" por cada uma.
Ao contrário do React, o Redux é alheio à sua camada de View, ele fica responsável apenas pelo State e pelas Actions da sua aplicação, dando a você total poder de escolha em relação à View.
A Store do Redux disponibiliza três métodos para interação com o State através de sua API pública:
Esse método permite que você adicione um listener à Store, que será chamado toda vez que houver uma alteração no State.
Analisando essas duas libs, nós conseguimos enxergar a forma como as suas se complementam e se integram uma com a outra, e também conseguimos ver que, mesmo se complementando, elas conseguem trabalhar de forma independente, deixando a aplicação muito menos acoplada.
cod (currentState, action) => newState
Observando a estrutura acima, percebemos que não precisamos depender do Redux para escrever nossos testes unitários, uma vez que você só precisará chamar um Reducer, passando um objeto para simular o State atual e um outro objeto que será a Action, e fazer uma asserção com o valor de retorno.
Na documentação do Redux, há uma sessão dedicada à escrita de testes, vale a pena conferir.
Os conceitos de programação funcional que o Redux traz ajudam muito a expandir nosso conhecimento e nossa forma de pensar como escrevemos nossa lógica, além de serem porta de entrada para muita gente nesse novo paradigma.
O Redux e o React se baseiam numa arquitetura de fluxo dados unidirecional, o que nos dá um controle bem maior, e não cria dificuldades na hora de integrarmos uma nova lib ao projeto.
Em um resumo geral, React e Redux nos ensinam a pensar em nossas aplicações de forma modular nos induzindo a identificar e separar as responsabilidades de uma forma bem intuitiva. A facilidade que o React traz para trabalharmos com componentes nos conduz a separá-los de uma forma melhor, o que por si só já resulta em um reaproveitamento maior do código. A forma como cada lib contém a sua responsabilidade nos permite aprender a desenvolver aplicações de uma forma desacoplada. Os conceitos que o React e o Redux introduzem trazem um ganho sem tamanho para nós, desenvolvedores.