12k
All articles

Como Corrigir o Erro Invalid Hook Call no React

Corrija erros invalid hook call no React verificando a stack trace, Rules of Hooks, cópias duplicadas de React e versões de react-dom.

OpenReplay Team
OpenReplay Team
Como Corrigir o Erro Invalid Hook Call no React

O erro de invalid hook call tem três causas comuns: uma violação das Rules of Hooks no seu próprio código, mais de uma cópia do React na aplicação, ou versões incompatíveis de react e react-dom. O stack trace indica qual delas verificar primeiro.

Ele costuma aparecer mesmo quando o código do seu componente está correto. Você faz npm link de uma biblioteca de componentes local, adiciona uma dependência ou reestrutura um monorepo, e o erro surge sem dizer qual das três causas você tem.

Pontos Principais

  • Se a chamada de hook que falha está no arquivo do seu próprio componente, o problema está em onde o hook é chamado; se está dentro de node_modules, o problema é quase sempre uma segunda cópia do React.
  • Execute npm ls react (ou pnpm why react, ou yarn why react); se a saída resolver mais de uma versão do React, cópias duplicadas são a causa e nenhuma alteração no código do componente vai resolver.
  • Uma biblioteca de componentes deve declarar o React em peerDependencies e excluí-lo do resultado do build; se ela empacotar seu próprio React, toda aplicação consumidora fica com duas cópias.
  • A regra de lint rules-of-hooks detecta chamadas de hooks fora de lugar antes de o código rodar, mas nenhum linter consegue detectar uma cópia duplicada do React, porque essa falha vive na árvore de dependências instalada, não no código-fonte.
  • Em produção o erro chega como o erro minificado #321, então decodifique-o no error decoder do React antes de tentar adivinhar a causa.

O Que Significa o Erro Invalid Hook Call?

O React lança esse erro sempre que um hook é executado fora do render de um componente de função, e a própria mensagem enumera as possibilidades:

Invalid hook call. Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

A página de aviso sobre invalid hook call do React cobre as três causas, além de uma seção genérica para os casos mais raros. O restante deste artigo as verifica na ordem que corresponde à forma como o erro costuma se apresentar.

Leia o Stack Trace Primeiro

Antes de mexer em qualquer configuração, responda a uma pergunta a partir do stack trace: o frame que chama o hook está nos seus próprios arquivos-fonte ou dentro de node_modules? Se aponta para o arquivo do seu componente, você tem uma violação das Rules of Hooks e a correção está no seu código. Se aponta para dentro de uma dependência que já funcionou antes, você quase certamente tem duas cópias do React, e nada que você editar em um componente mudará o resultado.

CausaComo confirmarCorreção
Violação das Rules of HooksO stack trace aponta para seus arquivosMova o hook para o nível superior de um componente
Duas cópias do Reactnpm ls react resolve duas versõesDeduplique a árvore (veja abaixo)
Incompatibilidade react/react-domnpm ls react react-dom mostra versões diferentesInstale ambos juntos

Corrigindo um Invalid Hook Call no Seu Próprio Código

Apenas duas regras produzem esse erro: hooks devem ser chamados durante o render de um componente de função (ou a partir de um hook customizado que um componente chama), e devem estar no nível superior desse componente, não dentro de um if, de um loop ou de uma função aninhada. Um hook em nível de módulo, em um event handler ou em uma função auxiliar comum quebra a primeira regra; um hook dentro de uma condição ou de um callback de .map quebra a segunda.

O caso da função auxiliar é o que mais surpreende as pessoas, porque o código parece razoável:

// Wrong: buildLink is a plain function, not a component
export function buildLink() {
  const { pathname } = useLocation(); // invalid hook call
  return `https://example.com${pathname}`;
}

// Right: call the hook in a component, pass the value down
function Page() {
  const { pathname } = useLocation();
  return <a href={buildLink(pathname)}>Canonical</a>;
}

export function buildLink(pathname) {
  return `https://example.com${pathname}`;
}

No caso do loop, a correção é estrutural: extraia um componente filho para que cada item tenha seu próprio estado.

// Wrong: one hook call per array item
function List({ items }) {
  return items.map((item) => {
    const [open, setOpen] = useState(false); // invalid hook call
    return <li key={item.id}>{item.name}</li>;
  });
}

// Right: each row is a component with its own state
function Row({ item }) {
  const [open, setOpen] = useState(false);
  return <li onClick={() => setOpen(!open)}>{item.name}</li>;
}

function List({ items }) {
  return items.map((item) => <Row key={item.id} item={item} />);
}

Por Que Duas Cópias do React Quebram os Hooks?

Hooks só funcionam quando sua aplicação e o react-dom carregam o mesmo módulo react. Se cada um recebe sua própria cópia, o React lança esse erro mesmo que todas as chamadas de hook no seu código estejam exatamente onde deveriam. Confirme isso antes de qualquer outra coisa:

npm ls react     # npm
pnpm why react   # pnpm
yarn why react   # yarn

O pnpm why e o yarn why trabalham de trás para frente, partindo de um pacote até o que o trouxe para a árvore, então você consegue ver exatamente qual dependência arrasta a segunda cópia. Duas situações respondem pela maioria das duplicações:

Um pacote local vinculado (linked). Uma biblioteca vinculada com npm link ou pnpm link resolve o React a partir do seu próprio node_modules, não do seu, e é por isso que o erro costuma aparecer no momento em que você faz link de uma biblioteca de componentes que funcionava perfeitamente quando instalada normalmente. A documentação do React cobre o caso do npm link, em que a correção é apontar a biblioteca para o React já instalado na aplicação. Em projetos Vite, liste os pacotes em resolve.dedupe e o Vite fixa cada um deles em uma única cópia obtida a partir da raiz do projeto:

// vite.config.js
export default {
  resolve: { dedupe: ['react', 'react-dom'] },
}

Uma biblioteca que distribui o React. Se um pacote declara react como dependência comum, ou o empacota no resultado do build, todo consumidor fica com duas cópias. A correção do lado da biblioteca é declarar o React em peerDependencies com o range que ela suporta e marcá-lo como external no build. A solução alternativa do lado da aplicação força uma única resolução, e o nome do campo depende do seu gerenciador de pacotes. O npm lê overrides, onde $react significa “a mesma versão que eu mesmo declaro para react”:

{ "overrides": { "react": "$react", "react-dom": "$react-dom" } }

O yarn, por sua vez, lê resolutions, com uma versão simples como valor. Não coloque os dois campos no mesmo arquivo; cada gerenciador ignora a chave do outro.

Incompatibilidade entre react e react-dom

react e react-dom são distribuídos como um par, então verifique ambos e instale-os em um único comando. Execute npm ls react react-dom; se as duas versões diferirem, reinstale-as juntas (npm install react react-dom) para que resolvam para a mesma release. Essa é a causa mais rápida de descartar, e descartá-la logo evita que você fique caçando bugs de código inexistentes.

Como Detectar Isso Mais Cedo com um Linter?

O pacote eslint-plugin-react-hooks sinaliza, em tempo de edição, todas as causas desse erro relacionadas ao código. Com a flat config do ESLint:

// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';

export default defineConfig([reactHooks.configs.flat.recommended]);

Em versões do ESLint anteriores à 9.0.0, o formato legado é "extends": ["plugin:react-hooks/recommended"]. Projetos Next.js já recebem essas regras através do eslint-config-next. A regra rules-of-hooks detecta chamadas de hooks condicionais e fora de lugar antes mesmo de o código rodar, mas nenhum linter consegue detectar uma cópia duplicada do React ou uma incompatibilidade de versões; essas falhas existem apenas na árvore de dependências instalada, então só aparecem em tempo de execução.

A Forma em Produção: Erro Minificado #321

Em um build de produção, esse erro chega como um código de erro minificado em vez da mensagem completa, então decodifique o código antes de presumir qual é o problema. O erro #321 do React se expande para o texto de invalid hook call; confirmar isso primeiro evita que você depure o invariante errado. A stack minificada raramente nomeia o componente que lançou o erro, o que torna o caso da cópia duplicada especialmente difícil de rastrear em produção. Uma ferramenta de session replay como o OpenReplay, que captura o erro de console junto com a rota e a interação que o precedeu, mostra qual árvore de componentes estava sendo montada no momento do erro, e isso normalmente aponta para o chunk carregado sob demanda (lazy-loaded) ou o widget de terceiros que trouxe a segunda cópia do React.

Comece pelo Stack Trace

Trate o erro como um problema de roteamento, não como um mistério: o stack trace o envia ou para o seu próprio componente (corrija o posicionamento do hook) ou para a árvore de dependências (execute npm ls react e deduplique). Comece por esse único comando; ele resolve a mais confusa das três causas em segundos, e tudo depois disso é uma correção conhecida.

Perguntas Frequentes

Um hook customizado precisa começar com 'use' para evitar o erro de invalid hook call?

Não. O prefixo 'use' nunca causa nem previne esse erro em tempo de execução, porque o React não verifica nomes de hooks em runtime. O prefixo importa para as ferramentas: o eslint-plugin-react-hooks depende dele para reconhecer hooks e aplicar as Rules of Hooks, então um hook customizado com nome errado escapa silenciosamente das verificações de lint. Renomeie-o com o prefixo para que as violações sejam sinalizadas em tempo de edição, e não no navegador.

Posso chamar hooks dentro de um class component?

Não. Hooks funcionam apenas em componentes de função e em hooks customizados chamados a partir deles, então chamar useState ou useContext dentro de um método de classe lança o erro de invalid hook call. Para usar um hook junto com uma classe que você não pode reescrever, crie um pequeno componente de função que chame o hook e passe o resultado para o componente de classe como props, ou converta a classe em um componente de função.

Duas cópias do React podem rodar na mesma página sem erros?

Sim. Duas aplicações em uma mesma página podem carregar cada uma o seu próprio React sem problemas, por exemplo quando times diferentes as distribuem separadamente. O erro só aparece quando um componente e a instância do react-dom que o renderiza discordam sobre qual módulo react estão usando. Cópias separadas são inofensivas por si só; elas quebram assim que compartilham uma única árvore de render.

Apagar node_modules e reinstalar corrige cópias duplicadas do React?

Somente quando a duplicação veio de um estado de instalação desatualizado ou conflitante, já que uma instalação limpa permite que o gerenciador de pacotes deduplique a árvore. Se uma dependência declara react como dependência comum, empacota o React no resultado do seu build, ou se você a vinculou localmente com npm link, a segunda cópia volta a cada instalação. Esses casos exigem uma entrada em overrides ou resolutions, uma correção de peerDependencies na biblioteca, ou deduplicação no nível do bundler.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.