Melhores Práticas de React Hooks
Um resumo condensado das 25 melhores práticas mais importantes, extraídas de todas as páginas desta seção.
Busque em todas as páginas da documentação
Um resumo condensado das 25 melhores práticas mais importantes, extraídas de todas as páginas desta seção.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
use: Comece cada nome de hook personalizado com use para que o plugin ESLint rules-of-hooks realmente aplique as regras; sem o prefixo, chamadas condicionais ilegais ainda quebram o React, mas nenhum erro de lint as detecta.as const: Retornar [value, setValue] as const dá aos consumidores readonly [T1, T2] com tipos de desestruturação corretos; sem as const, o TypeScript amplia para (T1 | T2)[] e a ordem da tupla é perdida.useActionState de react: No React 19, use useActionState de "react" - não o useFormState obsoleto de "react-dom" - e lembre-se que a nova assinatura retorna [state, formAction, isPending] com o estado de pendência embutido.prevState em Reducers de Ação: A assinatura (prevState, formData) => newState da ação substitui o estado inteiramente, então use ...prevState quando quiser apenas mesclar um campo; esquecer isso apaga todos os campos não tocados a cada submissão.useCallback Só Ajuda Consumidores Memoizados: useCallback é inútil por si só - ele previne re-renderizações apenas quando um filho React.memo ou uma dependência de efeito/memo realmente compara identidade, então não o espalhe em todos os manipuladores.dispatch Estável em Vez de useCallback: O dispatch do useReducer é permanentemente estável entre renderizações, então passá-lo adiante é frequentemente mais limpo do que envolver manipuladores em useCallback a cada nível; o mesmo se aplica aos setters de estado.<Context value={{…}}> re-renderiza cada consumidor a cada renderização pai; envolva o valor em useMemo - <Ctx value={useMemo(() => ({ theme, toggle }), [theme, toggle])}> - ou divida o estado e o atualizador em contextos separados para parar a avalanche.createContext(default) é usado apenas quando nenhum Provider envolve a árvore, não como o valor inicial do Provider; confie nisso para proteger contra providers ausentes com um hook personalizado que lança um erro se o contexto for nulo.useDeferredValue com React.memo: useDeferredValue adia a renderização dos consumidores, então o filho pesado deve ser envolvido em React.memo ou o pai o re-renderizará com o valor atual de qualquer maneira, tornando o hook um no-op.AbortController e aborte na limpeza para descartar resultados obsoletos: useEffect(() => { const ac = new AbortController(); fetch(url, { signal: ac.signal }); return () => ac.abort(); }, [url]).useEffect: Uma função assíncrona retorna um Promise, não uma limpeza, então defina uma função assíncrona interna e chame-a: useEffect(() => { async function load() { await fetch(url) } load(); }, [url]) - o React avisa sobre este padrão porque o Promise retornado nunca é aguardado para limpeza.useId Apenas para IDs de Acessibilidade: useId retorna um identificador estável em SSR por posição na árvore - perfeito para htmlFor, aria-describedby e conexões de formulário - mas nunca para keys de lista (um ID por chamada) e nunca dentro de loops ou condicionais.identifierPrefix para Múltiplas Raízes: Duas raízes React na mesma página geram valores useId conflitantes por padrão; passe identifierPrefix para cada createRoot/hydrateRoot para que micro-frontends ou widgets embutidos permaneçam distintos.useMemo Não é uma Garantia: O React pode descartar entradas de memoização (por exemplo, em componentes fora da tela) e recomputar mais tarde, então nunca confie em useMemo para correção - use-o para performance e estabilidade de referência, não para semântica de programa.useMemo: A fábrica é executada durante a renderização, então deve ser pura - logar, buscar ou escrever em localStorage dentro de useMemo quebra o modelo de renderização do React e se comporta de maneira inadequada sob Modo Estrito e renderização concorrente.addOptimistic Dentro de uma Transição: A atualização de useOptimistic desaparece silenciosamente a menos que addOptimistic seja executado dentro de uma ação de formulário, manipulador de Server Action, ou startTransition explícito; a sobreposição só persiste enquanto uma transição está pendente, com rollback automático em caso de erro.return { ...state, count: state.count + 1 } não state.count++; return state - nunca busque ou escreva em armazenamento, e sempre inclua um branch default: return state ou ações desconhecidas apagarão o estado para undefined.useState(() => computeInitial()) e useReducer(reducer, arg, init) para que o inicializador seja executado apenas na montagem; useState(computeInitial()) executa a computação em cada renderização e descarta o resultado.ref.current Durante a Renderização São Inseguras: Sob renderização concorrente, .current pode refletir uma passagem de renderização diferente da que está pintando, então leia refs dentro de efeitos, manipuladores de eventos ou efeitos de layout - não inline no corpo do componente.null Até o Commit: Um useRef anexado a um nó DOM é null durante a primeira renderização, então tentar tocar ref.current.focus() inline lança um erro; faça isso em useEffect, useLayoutEffect, ou um manipulador, e lembre-se que a ref de um elemento renderizado condicionalmente é nula enquanto oculto.setState(prev => …): Três chamadas diretas de setCount(count + 1) no mesmo manipulador incrementam apenas uma vez porque todas fecham sobre o mesmo count obsoleto; a forma atualizadora setCount(prev => prev + 1) compõe corretamente sob o batching automático do React 18+.useState Preguiçoso Precisa de um Callback: useState(() => parseJSON(localStorage.x)) executa o parse apenas na montagem; useState(parseJSON(localStorage.x)) o executa em cada renderização e descarta todos, exceto o primeiro - o que é caro e pode lançar um erro durante SSR.startTransition apenas adia os setters de estado do React - refs, stores externos e escritas síncronas no DOM não são afetados; lembre-se também que o React 18 requer um callback síncrono enquanto o React 19 permite callbacks async nativamente.use() um Promise Estável: use(promise) trata um objeto promise fresco como uma nova requisição pendente, então criar um promise inline na renderização causa suspensão infinita; o promise deve vir de props, um pai, um cache, ou uma ref estável - e sempre ficar sob um Suspense mais um error boundary.Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥