Cómo Solucionar el Error "Invalid Hook Call" en React
Corrige los errores invalid hook call de React revisando la pila, las Rules of Hooks, copias duplicadas de React y versiones de react-dom.
El error de llamada inválida a un hook tiene tres causas comunes: una violación de las Reglas de los Hooks en tu propio código, más de una copia de React en la aplicación o versiones no coincidentes de react y react-dom. El stack trace te indica cuál revisar primero.
Suele aparecer cuando el código de tus propios componentes está correcto. Haces npm link a una biblioteca de componentes local, agregas una dependencia o reestructuras un monorepo, y el error surge sin indicarte cuál de las tres causas tienes.
Puntos Clave
- Si la llamada al hook que falla está en el archivo de tu propio componente, el problema está en el lugar donde se llama al hook; si está dentro de
node_modules, el problema es casi siempre una segunda copia de React. - Ejecuta
npm ls react(opnpm why react, oyarn why react); si la salida resuelve más de una versión de React, las copias duplicadas son la causa y ningún cambio en el código de los componentes lo solucionará. - Una biblioteca de componentes debe declarar React en
peerDependenciesy excluirlo de su salida de compilación; si empaqueta su propio React, cada aplicación consumidora obtiene dos copias. - La regla de lint
rules-of-hooksdetecta llamadas a hooks mal ubicadas antes de que el código se ejecute, pero ningún linter puede detectar una copia duplicada de React, porque esa falla vive en el árbol de dependencias instalado, no en el código fuente. - En producción el error llega como el error minificado #321, así que decodifícalo en el decodificador de errores de React antes de conjeturar sobre la causa.
¿Qué Significa el Error “Invalid Hook Call”?
React lanza este error cada vez que un hook se ejecuta fuera del render de un componente de función, y el propio mensaje enumera las posibilidades:
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
La página de advertencia sobre llamadas inválidas a hooks de React cubre las tres, además de una sección general para los casos más raros. El resto de este artículo las revisa en el orden que coincide con la forma en que el error suele presentarse.
Lee Primero el Stack Trace
Antes de tocar cualquier configuración, responde una pregunta a partir del stack trace: ¿el frame que llama al hook está en tus propios archivos fuente, o dentro de node_modules? Si apunta al archivo de tu componente, tienes una violación de las Reglas de los Hooks y la solución está en tu código. Si apunta a una dependencia que antes funcionaba, casi con certeza tienes dos copias de React, y nada de lo que edites en un componente cambiará el resultado.
| Causa | Cómo confirmarlo | Solución |
|---|---|---|
| Violación de las Reglas de los Hooks | El stack trace apunta a tus archivos | Mueve el hook al nivel superior de un componente |
| Dos copias de React | npm ls react resuelve dos versiones | Deduplica el árbol (ver más abajo) |
Desajuste de react/react-dom | npm ls react react-dom muestra versiones distintas | Instala ambos juntos |
Cómo Solucionar una Llamada Inválida a un Hook en tu Propio Código
Solo dos reglas producen este error: los hooks deben llamarse durante el render de un componente de función (o desde un hook personalizado que un componente invoque), y deben estar en el nivel superior de ese componente, no dentro de un if, un bucle o una función anidada. Un hook a nivel de módulo, en un manejador de eventos o en una función auxiliar corriente rompe la primera regla; un hook dentro de una condición o de un callback de .map rompe la segunda.
El caso de la función auxiliar es el que sorprende a la gente, porque el código parece razonable:
// 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}`;
}
Para el caso del bucle, la solución es estructural: extrae un componente hijo para que cada elemento sea dueño de su propio 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 Qué Dos Copias de React Rompen los Hooks?
Los hooks solo funcionan cuando tu aplicación y react-dom cargan el mismo módulo react. Si cada uno obtiene su propia copia, React lanza este error aunque cada llamada a un hook en tu código esté exactamente donde debe estar. Confírmalo antes que nada:
npm ls react # npm
pnpm why react # pnpm
yarn why react # yarn
pnpm why y yarn why trabajan hacia atrás desde un paquete hasta aquello que lo introdujo, de modo que puedes ver exactamente qué dependencia arrastra la segunda copia. Dos situaciones explican la mayoría de los duplicados:
Un paquete local enlazado. Una biblioteca enlazada con npm link o pnpm link resuelve React desde su propio node_modules, no desde el tuyo, y por eso el error suele aparecer justo cuando enlazas una biblioteca de componentes que funcionaba perfectamente instalada de forma normal. La documentación de React cubre el caso de npm link, donde la solución es apuntar la biblioteca al React ya instalado en la aplicación. En proyectos con Vite, lista los paquetes en resolve.dedupe y Vite fijará cada uno de ellos a una única copia tomada de la raíz del proyecto:
// vite.config.js
export default {
resolve: { dedupe: ['react', 'react-dom'] },
}
Una biblioteca que incluye React. Si un paquete declara react como una dependencia normal, o lo empaqueta en su salida de compilación, cada consumidor obtiene dos copias. La solución del lado de la biblioteca consiste en declarar React en peerDependencies con el rango que soporta y marcarlo como externo en la compilación. La solución alternativa del lado de la aplicación fuerza una única resolución, y el nombre del campo depende de tu gestor de paquetes. npm lee overrides, donde $react significa “la misma versión que yo mismo declaro para react”:
{ "overrides": { "react": "$react", "react-dom": "$react-dom" } }
yarn en cambio lee resolutions, con una versión simple como valor. No pongas ambos campos en un mismo archivo; cada gestor ignora la clave del otro.
Versiones No Coincidentes de react y react-dom
react y react-dom se publican como un par, así que revisa ambos e instálalos en un solo comando. Ejecuta npm ls react react-dom; si las dos versiones difieren, reinstálalos juntos (npm install react react-dom) para que resuelvan a la misma release. Esta es la causa más rápida de descartar, y descartarla temprano evita que persigas errores de código fantasma.
¿Cómo Detectarlo Antes con un Linter?
El paquete eslint-plugin-react-hooks señala todas las causas de este error que tienen forma de código en tiempo de edición. Con la configuración flat de ESLint:
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([reactHooks.configs.flat.recommended]);
En versiones de ESLint anteriores a la 9.0.0, la forma heredada es "extends": ["plugin:react-hooks/recommended"]. Los proyectos con Next.js ya reciben estas reglas a través de eslint-config-next. La regla rules-of-hooks detecta llamadas condicionales y mal ubicadas a hooks antes de que el código llegue a ejecutarse, pero ningún linter puede detectar una copia duplicada de React ni un desajuste de versiones; esas fallas existen únicamente en el árbol de dependencias instalado, por lo que solo se manifiestan en tiempo de ejecución.
La Forma en Producción: Error Minificado #321
En una compilación de producción este error llega como un código de error minificado en lugar del mensaje completo, así que decodifica el código antes de asumir cuál es el problema. El error #321 de React se expande al texto de la llamada inválida a un hook; confirmarlo primero evita que depures el invariante equivocado. El stack minificado rara vez nombra el componente que lanzó el error, lo que hace que el caso de la copia duplicada sea especialmente difícil de rastrear en producción. Una herramienta de session replay como OpenReplay, que captura el error de consola junto con la ruta y la interacción que lo precedió, muestra qué árbol de componentes se estaba montando en el momento del fallo, y eso suele apuntar al chunk cargado de forma diferida o al widget de terceros que introdujo la segunda copia de React.
Empieza por el Stack Trace
Trata el error como un problema de enrutamiento, no como un misterio: el stack trace te envía o bien a tu propio componente (corrige la ubicación del hook) o bien al árbol de dependencias (ejecuta npm ls react y deduplica). Empieza con ese único comando; resuelve en segundos la más confusa de las tres causas, y todo lo que viene después es una solución conocida.
Preguntas Frecuentes
¿Un hook personalizado necesita comenzar con 'use' para evitar el error de llamada inválida a un hook?
No. El prefijo 'use' nunca causa ni previene este error en tiempo de ejecución, porque React no comprueba los nombres de los hooks en tiempo de ejecución. El prefijo importa para las herramientas: eslint-plugin-react-hooks se apoya en él para reconocer hooks y hacer cumplir las Reglas de los Hooks, de modo que un hook personalizado mal nombrado escapa silenciosamente a las comprobaciones de lint. Renómbralo con el prefijo para que las violaciones se señalen en tiempo de edición en lugar de en el navegador.
¿Puedo llamar hooks dentro de un componente de clase?
No. Los hooks funcionan solo en componentes de función y en hooks personalizados llamados desde ellos, así que llamar a useState o useContext dentro de un método de clase lanza el error de llamada inválida a un hook. Para usar un hook junto a una clase que no puedes reescribir, crea un pequeño componente de función que llame al hook y pase el resultado al componente de clase como props, o convierte la clase en un componente de función.
¿Pueden ejecutarse dos copias de React en la misma página sin errores?
Sí. Dos aplicaciones en una misma página pueden cargar cada una su propio React sin problema alguno, por ejemplo cuando equipos distintos las publican por separado. El error aparece únicamente cuando un componente y la instancia de react-dom que lo renderiza discrepan sobre qué módulo react están usando. Las copias separadas están bien por sí solas; se rompen en cuanto comparten un único árbol de render.
¿Eliminar node_modules y reinstalar soluciona las copias duplicadas de React?
Solo cuando el duplicado provino de un estado de instalación obsoleto o conflictivo, ya que una instalación limpia permite al gestor de paquetes deduplicar el árbol. Si una dependencia declara react como una dependencia normal, empaqueta React en su salida de compilación, o la enlazaste localmente con npm link, la segunda copia vuelve en cada instalación. Esos casos necesitan una entrada de overrides o resolutions, una corrección de peerDependencies en la biblioteca, o deduplicación a nivel del bundler.
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