Invalid Hook Call Warning
Probablemente llegaste aquí porque obtuviste el siguiente mensaje de error:
Hooks can only be called inside the body of a function component.
Hay tres razones comunes por las cuales podrías estar viéndolo:
- Tienes versiones incongruentes de React y React DOM.
- Estás rompiendo las Reglas de los Hooks.
- Tienes más de una copia de React en la misma aplicación.
Demos un vistazo a cada uno de estos casos.
Versiones incongruentes de React y React DOM
Puede que estés usando una versión de react-dom
(< 16.8.0) o react-native
(< 0.59) que no es compatible con los Hooks. Puedes correr npm ls react-dom
o npm ls react-native
en el directorio de tu aplicación para verificar qué versión estás usando. Si encuentras más de una de ellas, esto también puede causar problemas (más abajo, más información sobre esto).
Rompiendo las reglas de los Hooks
Solamente puedes llamar Hooks mientras React está renderizando un componente de función:
- ✅ Llámalos en el nivel superior en el cuerpo de un componente de función.
- ✅ Llámalos en el nivel superior en el cuerpo de un Hook personalizado.
Lee más acerca de esto en las Reglas de los Hooks.
function Counter() {
// ✅ Bien: nivel superior en un componente de función const [count, setCount] = useState(0); // ...
}
function useWindowWidth() {
// ✅ Bien: Nivel superior en un Hook personalizado const [width, setWidth] = useState(window.innerWidth); // ...
}
Para evitar confusiones, no se admite llamar Hooks en otros casos:
- 🔴 No llames Hooks en componentes de clase.
- 🔴 No los llames en manejadores de eventos.
- 🔴 No llames Hooks dentro de funciones pasadas a
useMemo
,useReducer
, ouseEffect
.
Si rompes estas reglas, podrías ver el error.
function Bad1() {
function handleClick() {
// 🔴 Mal: Dentro de un manejador de eventos (para arreglarlo, muévelo afuera!) const theme = useContext(ThemeContext); }
// ...
}
function Bad2() {
const style = useMemo(() => {
// 🔴 Mal: Dentro de useMemo (para arreglarlo, muévelo afuera!) const theme = useContext(ThemeContext); return createStyle(theme);
});
// ...
}
class Bad3 extends React.Component {
render() {
// 🔴 Mal: En un componente de clase useEffect(() => {}) // ...
}
}
Puedes usar el plugin eslint-plugin-react-hooks
para identificar algunos de estos errores.
Nota
Los Hooks personalizados podrían llamar otros Hooks (ese es su propósito). Esto funciona porque los Hooks personalizados también se supone que deben llamarse únicamente mientras un componente de función se está renderizando.
React duplicado
Para que los Hook funcionen, el import de react
de tu código debe resolver al mismo módulo que el import de react
dentro del paquete react-dom
.
Si estos imports de react
resuelven a dos objetos exports diferentes, verás esta advertencia. Esto podría suceder si accidentalmente resultas con dos copias del paquete react
.
Si usas Node para gestión de paquetes, puedes correr este comando en el directorio de tu proyecto:
npm ls react
Si ves más de un React, tendrás que descubrir por qué está sucediendo esto y arreglar tu árbol de dependencias. Por ejemplo, quizá una biblioteca que estás usando especifica incorrectamente react
como una dependencia (en lugar de una peer dependency). Hasta que esa biblioteca sea arreglada, Yarn resolutions es una posible solución temporal.
También puedes intentar depurar este problema agregando algunos logs y reiniciando el servidor de desarrollo:
// Agrega esto en node_modules/react-dom/index.js
window.React1 = require('react');
// Agrega esto en tu componente
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);
Si imprime false
entonces probablemente tienes dos React y debes averiguar por qué. Este issue incluye varias razones comunes encontradas por la comunidad.
Este problema también ocurre cuando usas npm link
o un equivalente. En ese caso, tu bundler podría “ver” dos Reacts — uno en la carpeta de aplicación y otro en la de bibliotecas. Asumiendo que myapp
y mylib
son carpetas hermanas, una solución posible es correr npm link ../myapp/node_modules/react
en mylib
. Esto debería hacer que la biblioteca use la copia de React de la aplicación.
Nota
En general, React admite usar múltiples copias independientes en una página (por ejemplo, si una página y un widget externo lo usan). Sólo falla si
require('react')
resuelve diferente entre un componente y la copia dereact-dom
con la que fue renderizado.
Otras causas
Si nada de esto funciona, por favor agrega un comentario en este issue y trataremos de ayudar. Intenta crear un pequeño ejemplo que reproduzca el problema — podrías encontrar la causa mientras lo haces.