Caso 05 React · JavaScript · jQuery
Modernização React: fluxos assíncronos que se leem de cima para baixo
Participei da migração de um frontend legado em JavaScript e jQuery para React. Além de componentes reutilizáveis, a migração é uma chance de decidir como fluxos assíncronos serão lidos por quem mantém o código.
Contexto
No DPMS, parte da interface dependia de JavaScript e jQuery legados. A equipe migrou essas telas para React, com objetivos de manutenibilidade, reutilização de componentes, extensibilidade e um modelo de renderização mais claro.
A migração foi um trabalho de equipe. Participei dela e da discussão sobre como estruturar os componentes e os fluxos.
Problema
Trocar jQuery por React não garante, sozinho, código mais fácil de manter. Um ponto específico costuma pesar: ações do usuário que disparam várias chamadas de API dependentes.
Cada etapa pode falhar e precisa de feedback, e a operação final só deve acontecer quando todas as etapas necessárias tiverem sucesso. Se esse processo for dividido em vários useEffect que reagem a mudanças de estado, a sequência deixa de estar escrita em algum lugar: ela precisa ser deduzida.
Minha responsabilidade
Participei da migração e ajudei a definir a arquitetura React que substituiu as interfaces legadas, com atenção à reutilização de componentes, à separação de responsabilidades e à legibilidade dos fluxos assíncronos.
Não fui o único autor da migração; este caso registra uma decisão de engenharia que defendo e aplico.
Decisão
Quando uma ação do usuário dispara um processo, prefiro tornar a orquestração explícita: uma função com nome que revela a intenção, etapas em sequência legível e tratamento de erro no ponto em que a falha acontece.
Dependendo do problema, um handler de evento, uma função assíncrona, um custom hook ou uma abstração de data fetching pode ser a melhor forma. Efeitos continuam úteis para sincronizar com sistemas externos, como assinaturas e conexões, com cleanup ou cancelamento quando apropriado.
Alternativas e trade-offs
Análise atual, escrita para este site. Não é um registro das alternativas avaliadas na época.
- Efeitos encadeados podem funcionar e às vezes parecem mais "reativos". O custo aparece na leitura: as dependências ficam implícitas em arrays de dependência e flags de estado, e o efeito não tem um nome que diga qual processo de negócio executa.
- Uma função explícita concentra o fluxo, mas pede cuidado com cancelamento quando o componente sai da tela e com estados de carregamento. Custom hooks ajudam a nomear essas responsabilidades sem voltar a espalhá-las.
- O ponto é julgamento e legibilidade, não uma regra contra
useEffect.
Implementação
Os critérios que levo para revisões e decisões de arquitetura em React:
Nomes que revelam intenção
O processo de negócio aparece como uma função, por exemplo
approveReport, em vez de surgir da combinação de efeitos.Sequência visível
Chamadas dependentes ficam em ordem no código; quem lê não precisa reconstruir a cadeia a partir de mudanças de estado.
Falhas tratadas onde acontecem
Cada etapa pode falhar com uma mensagem útil, e a operação final só roda quando as anteriores tiveram sucesso.
Efeitos para sincronização
useEffectfica reservado para sincronizar com sistemas externos, com cleanup quando necessário.
Visualização
Efeitos encadeados
const [report, setReport] = useState(null);const [metrics, setMetrics] = useState(null);const [pdf, setPdf] = useState(null);const handleApprove = () => saveReport(draft) .then(setReport) .catch(showError);useEffect(() => { if (!report) return; recalculateMetrics(report.id) .then(setMetrics) .catch(showError);}, [report]);useEffect(() => { if (!metrics) return; generatePdf(metrics.reportId) .then(setPdf) .catch(showError);}, [metrics]);useEffect(() => { if (!pdf) return; publish(pdf.reportId).catch(showError);}, [pdf]);Pontos de leitura para entender a sequência: 3 efeitos, 3 estados intermediários e os arrays de dependência
Função nomeada
async function approveReport(draft: Draft) { const report = await saveReport(draft); const metrics = await recalculateMetrics(report.id); const pdf = await generatePdf(metrics.reportId); await publish(pdf.reportId);}const handleApprove = async () => { try { await approveReport(draft); showSuccess(); } catch (error) { showError(error); }};Pontos de leitura para entender a sequência: 1 função, lida de cima para baixo
- Salvar relatório Aguardando
- Recalcular indicadores Aguardando
- Gerar PDF Aguardando
- Publicar Aguardando
A interação fica disponível quando o JavaScript carrega. O código acima já descreve os dois fluxos.
useEffect continua adequado para sincronizar com sistemas externos, como assinaturas e conexões. Custom hooks também podem dar nome a responsabilidades.Resultado
A migração buscou componentes reutilizáveis e um modelo de renderização mais claro que o das telas em jQuery. O resultado que destaco aqui é qualitativo: um critério explícito para que fluxos assíncronos possam ser lidos, revisados e alterados sem caçar dependências pela tela.
A comparação interativa acima usa código ilustrativo e chamadas simuladas; ela não reproduz o código do projeto.