Refatorização React Native: de 800 linhas para 100 linhas
Há um momento em que você abre um arquivo e sente fisicamente o peso da dívida técnica. Minha tela principal tinha 800 linhas. Apenas um arquivo. Lógica de negócios, renderização de UI, gerenciamento de estado, chamadas de API — tudo misturado em um componente monolítico impossível de manter. E não era um caso isolado: eu tinha 16 telas no mesmo estado.
O que vou te contar aqui é como transformei um código caótico em uma arquitetura limpa. Não em uma revolução espetacular, mas em um refactoring metódico, tela por tela, erro por erro.
Pontos-chave a reter:
- Um hook customizado por tela separa a lógica de negócios da renderização da UI
- O padrão "orquestrador + subcomponentes" reduz os arquivos de 800 para 100 linhas
- ESLint com regras estritas (zeroany, promessas awaited) elimina bugs silenciosos
- A dívida técnica é um custo real mensurável em tempo, bugs e motivação
- Refatorar tela por tela permite entregar continuamente sem quebrar o existente
Por que um componente de 800 linhas é um problema?
A resposta óbvia: é ilegível. Mas o verdadeiro problema é mais insidioso. Um componente monolítico:
- Esconde bugs: quando tudo está no mesmo arquivo, os efeitos colaterais são imprevisíveis. Modificar a lógica de filtro pode quebrar a animação de transição sem que você entenda o porquê.
- Atrasa o desenvolvimento: cada modificação exige a compreensão de todo o arquivo. Você passa 20 minutos "lendo" antes de poder escrever 5 linhas.
- Torna a depuração impossível: quando um bug ocorre, você tem 800 linhas de suspeitos. Não há fronteira clara entre as responsabilidades.
- Deteriora a motivação: abrir um arquivo monstruoso todas as manhãs é pesado. É o tipo de atrito invisível que atrasa um projeto indie.
"Você está sozinho, quem vai ler este código?" Eu. Eu daqui a três meses, quando tiver esquecido por que esta tela faz o que faz. A dívida técnica não é uma metáfora. É um custo real que se paga em tempo, em bugs e em motivação.
Que estratégia de divisão adotar?
Eu refatorei as 16 telas seguindo uma estrutura consistente. O padrão é simples, mas poderoso:
1. Um hook customizado para a lógica de negócios
Cada tela tem seu hook: useTaskDetail, useFeedScreen, useGroupScreen... O hook encapsula toda a lógica: carregamento de dados, gerenciamento de estado, callbacks. O componente apenas renderiza JSX.
Antes:
// TaskDetailScreen.tsx - 800 linhas
const TaskDetailScreen = () => {
const [task, setTask] = useState(null);
const [loading, setLoading] = useState(true);
const [editing, setEditing] = useState(false);
// ... 50 outros useState
// ... 200 linhas de useEffect
// ... 300 linhas de handlers
// ... 250 linhas de JSX
}
Depois:
// TaskDetailScreen.tsx - 100 linhas
const TaskDetailScreen = () => {
const { task, loading, editing, handlers } = useTaskDetail();
return (
<TaskDetailHeader task={task} onEdit={handlers.edit} />
<TaskDetailBody task={task} />
<TaskDetailActions handlers={handlers} />
);
}
O hook useTaskDetail tem 200 linhas. Os subcomponentes têm de 50 a 100 linhas cada. No total, são mais linhas do que antes — mas cada arquivo é compreensível isoladamente.
2. Subcomponentes para a renderização
Cada seção visual da tela se torna um componente dedicado: TaskDetailHeader, TaskDetailBody, TaskDetailActions. Cada um recebe suas props e conhece apenas sua responsabilidade.
Este padrão é diretamente inspirado no Thinking in React oficial. Não é novo, mas é incrível como o esquecemos quando estamos com pressa para entregar.
3. Um componente principal para a orquestração
O componente principal se torna um maestro de 100 linhas: ele chama o hook, distribui as props para os subcomponentes e gerencia o layout global. Nada mais.
Como passar de 71 para 0 erros ESLint?
Paralelamente ao refactoring das telas, executei o ESLint no backend com regras estritas. Resultado: 71 erros.
A tentação: desativar as regras que geram mais erros. É o que 90% dos desenvolvedores fazem. Eu fiz o contrário: corrigi cada erro individualmente.
As correções mais impactantes
Eliminação de any: cada any no TypeScript é uma bomba-relógio. Compila, passa nos testes, mas trava em produção quando um valor inesperado chega. Substituí cada any por um tipo explícito. Alguns casos exigiram a criação de interfaces dedicadas, mas o resultado é um código autodocumentado.
Promessas corretamente awaited: chamadas assíncronas sem await que falhavam silenciosamente. Sem erro, sem aviso — apenas um comportamento incorreto em produção. O ESLint detectou 12 casos de promessas não awaited. 12 bugs potenciais eliminados em uma hora.
Variáveis não utilizadas: imports esquecidos, variáveis declaradas e nunca usadas. Não são bugs, mas ruído que torna o código mais difícil de ler. Cada linha inútil é uma distração para o "você do futuro" que tenta entender o código.
Deve-se refatorar tudo de uma vez ou progressivamente?
Progressivamente. Sempre progressivamente. Um "big bang refactoring" onde você reescreve tudo em uma semana é a receita para o desastre para um desenvolvedor solo. Você quebra tudo, não consegue mais entregar, e se um bug crítico surgir em produção, você não consegue corrigi-lo rapidamente.
Meu método:
- Escolher a tela mais dolorosa (aquela que você teme abrir)
- Extrair o hook customizado primeiro (a lógica, não a renderização)
- Testar se tudo ainda funciona exatamente como antes
- Extrair os subcomponentes um por um
- Entregar a tela refatorada, passar para a próxima
Cada tela refatorada levava entre 2 horas e meio dia. As mais complexas (o Feed, a tela de grupo) levaram um dia inteiro. Mas a cada etapa, o aplicativo permanecia funcional e entregável.
Este fluxo de trabalho é semelhante ao que Martin Fowler descreve em seu livro sobre refactoring: transformações pequenas, incrementais e sempre reversíveis.
Quais ganhos concretos após o refactoring?
Após duas semanas de trabalho:
- 16 telas refatoradas: cada uma segue o mesmo padrão (hook + subcomponentes + orquestrador)
- 0 erro ESLint no backend (contra 71 antes)
- Tempo de compreensão dividido por 3: abrir uma tela e entender o que ela faz leva 2 minutos em vez de 10
- Bugs reduzidos: as 3 semanas seguintes foram as mais estáveis do projeto
- Motivação recuperada: abrir um arquivo limpo pela manhã muda tudo
O ganho menos mensurável, mas o mais importante: a confiança. Após o refactoring, eu não tinha mais medo de modificar uma tela. Eu sabia exatamente onde cada coisa estava, quais eram as dependências e o que uma mudança iria impactar.
Como a dívida técnica afeta um projeto solo?
A dívida técnica em um projeto solo tem efeitos específicos que não são encontrados em equipe:
Sem rede de segurança. Em equipe, um colega pode revisar seu código, detectar uma regressão ou corrigir um bug quando você está ausente. Sozinho, se seu código é incompreensível, é você — e somente você — quem paga o preço.
O efeito composto. Cada atalho que você toma hoje se multiplica. Um any aqui, um // TODO: fix later ali, e em alguns meses, você tem um código onde cada modificação leva 3x mais tempo do que deveria.
A espiral de desmotivação. Código sujo → bugs → frustração → patches rápidos → código ainda mais sujo. Eu vi essa espiral em outros projetos. O refactoring a quebrou completamente.
É a mesma lógica que aplico à reestruturação do banco de dados e à auditoria de segurança: investir tempo agora para ganhar exponencialmente mais tarde.
Quais regras ESLint recomendar para um projeto React Native?
Aqui estão as regras que tiveram o maior impacto na qualidade do código TAMSIV:
@typescript-eslint/no-explicit-any: proíbeany. Não negociável.@typescript-eslint/no-floating-promises: detecta promessas não awaited. Crítico para evitar bugs silenciosos.@typescript-eslint/no-unused-vars: elimina o ruído do código morto.react-hooks/exhaustive-deps: verifica as dependências dos hooks. Essencial no React Native.no-console: força o uso de um logger em vez deconsole.logem produção.
Ative todas no modo "error", não "warn". Um aviso é ignorado. Um erro é corrigido.
O que aprendi com esta semana de refactoring
Esta semana foi a melhor do projeto. Não a mais emocionante — nenhuma funcionalidade visível para o usuário. Mas a mais impactante para o futuro. Cada funcionalidade que construí depois — o sistema de gamificação, os grupos hierárquicos, o agenda colaborativa — se beneficiou desta arquitetura limpa.
Se você é um desenvolvedor solo e está adiando um refactoring porque "não entrega nada visível", pare. É o investimento mais rentável que você pode fazer. Não é glamoroso, não é emocionante, mas é fundamentalmente necessário.
A melhor semana do projeto. E eu repito sem hesitação.
FAQ
Quanto tempo leva um refactoring de 16 telas?
Para mim, cerca de duas semanas em tempo integral. Cada tela leva entre 2 horas e um dia, dependendo de sua complexidade. A tela mais simples (um formulário básico): 2 horas. A mais complexa (o Feed com gamificação): um dia completo.
O refactoring introduz novos bugs?
Esse é o principal risco. Para minimizá-lo, eu refatorava uma tela por vez e testava manualmente cada fluxo antes de passar para o próximo. Nenhuma regressão importante apareceu durante as duas semanas de refactoring.
É preciso escrever testes antes de refatorar?
Idealmente, sim. Na realidade, em um projeto solo com 16 telas para refatorar, escrever testes exaustivos antes do refactoring dobraria o tempo. Optei por testes manuais rigorosos e testes automatizados após o refactoring, na arquitetura limpa.
O padrão hook + subcomponentes funciona para todas as telas?
Sim, com variações. Algumas telas simples não precisam de subcomponentes — o hook sozinho é suficiente. Outras telas complexas têm vários níveis de subcomponentes. O princípio permanece o mesmo: separar lógica e renderização.
O ESLint atrasa o desenvolvimento diário?
Nas primeiras horas, sim — o tempo para corrigir os erros existentes. Depois, é o contrário: o ESLint acelera o desenvolvimento detectando erros antes mesmo de iniciar o aplicativo. É como um copiloto que corrige sua trajetória em tempo real.