Todos os posts
Reactsábado, 15 de junho de 2024

Domine React Hooks

Uso disciplinado de Hooks em 2024: efeitos como sincronização, concorrência segura, eliminação de estado derivado, custom hooks como API de domínio e controle real de re-render.

Leonardo David

Leonardo David

14 min de leitura

Hooks são poderosos porque tornam composição, estado e efeitos parte do mesmo modelo mental. Eles também são fonte de bugs sutis quando useEffect vira fluxo de controle em vez de sincronização com sistemas externos.

Desde o React 18, com renderização concorrente e Strict Mode executando efeitos duas vezes em desenvolvimento, o uso descuidado de Hooks expõe problemas arquiteturais. O objetivo não é 'fazer funcionar', mas garantir previsibilidade sob concorrência, re-render e transições.

1) Rules of Hooks são invariantes estruturais

Hooks não são funções comuns. A ordem de chamada precisa ser estável entre renders. Chamadas condicionais, dentro de loops ou após retornos antecipados quebram o contrato do reconciler.

// erro estrutural clássico
if (isOpen) {
useEffect(() => {
// ❌ inválido
}, []);
}

2) useEffect é sincronização com o mundo externo

useEffect não é ferramenta para fluxo de negócio ou derivação de estado. Ele existe para sincronizar com sistemas externos: DOM imperativo, eventos globais, subscriptions, timers, analytics, network side-effects.

Se algo pode ser derivado de props e state, calcule durante o render. Estado derivado em efeito cria inconsistência temporal.

// anti-pattern: estado derivado via efeito
// ❌ Evite
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(first + " " + last);
}, [first, last]);
// correto: derivação no render
const fullName = first + " " + last;

3) Efeitos devem ser idempotentes

Em Strict Mode (dev), efeitos executam duas vezes para detectar efeitos não idempotentes. Código que depende de 'rodar uma vez' está incorreto por definição.

// efeito com cleanup adequado
useEffect(() => {
const controller = new AbortController();
fetch("/api/data", { signal: controller.signal });
return () => {
controller.abort();
};
}, []);

4) Dependências não são sugestão — são contrato

Ignorar eslint-plugin-react-hooks cria closures obsoletas (stale closures). Se um valor é usado dentro do efeito, ele deve estar na lista de dependências ou ser estabilizado.

// stale closure clássico
useEffect(() => {
const id = setInterval(() => {
console.log(count); // pode ficar desatualizado
}, 1000);
return () => clearInterval(id);
}, []);

5) useMemo e useCallback são otimizações, não arquitetura

Memoização excessiva aumenta complexidade cognitiva. Só use quando: (1) evitar recomputação pesada, (2) manter identidade estável para memoização descendente, (3) evitar rerender caro.

6) Estado local é default. Compartilhamento exige fronteira clara

Elevar estado cedo demais cria acoplamento estrutural. Prefira estado local até haver necessidade real de coordenação entre múltiplas áreas.

7) useRef não é estado invisível

useRef é para armazenar valores mutáveis que não causam re-render. Não use como atalho para evitar arquitetura adequada.

// uso correto de ref para valor mutável
const latestValue = useRef(value);
useEffect(() => {
latestValue.current = value;
}, [value]);

8) Custom hooks são APIs de domínio

Um hook deve comunicar intenção de produto. useCheckoutFlow, usePermissionGate, useAutoSaveDraft são contratos semânticos. Isso reduz vazamento de implementação.

// custom hook como boundary de domínio
function useCheckoutFlow() {
const [step, setStep] = useState<"cart" | "payment" | "confirm">("cart");
const next = () => {
setStep((s) =>
s === "cart" ? "payment" :
s === "payment" ? "confirm" :
s
);
};
return { step, next };
}

9) Concurrency e transições exigem previsibilidade

Com startTransition e render concorrente, atualizações podem ser interrompidas. Hooks precisam ser determinísticos. Evite efeitos com dependência implícita de ordem temporal.

10) Separação clara: render puro vs side effects

Render deve ser função pura de props + state. Effects conectam o componente ao mundo externo. Misturar ambos gera comportamento difícil de raciocinar sob re-render.

Os melhores hooks descrevem comportamento de produto com efeitos previsíveis e fronteiras explícitas com o mundo externo.

Leonardo David

Checklist de disciplina com Hooks

// verificação arquitetural
const hooksChecklist = [
"Hook não é chamado condicionalmente?",
"useEffect sincroniza algo externo?",
"Não existe estado derivado desnecessário?",
"Dependências estão completas?",
"Efeito é idempotente?",
"Memoização tem justificativa mensurável?",
"Custom hook expressa domínio e não detalhe técnico?",
];

Dominar Hooks não é memorizar APIs. É entender o modelo mental do React: render puro, efeitos previsíveis, identidade estável e concorrência segura. Quando essa disciplina é aplicada, a base do produto se torna mais previsível, testável e evolutiva.

ReactHooksArquiteturaManutenibilidade
Leonardo David

Escrito por Leonardo David

Engenheiro Full Stack. Atuo construindo produtos de alta performance na intersecção entre frontend, backend e estratégia de produto.