Reglas de los Hooks

Probablemente estás aquí porque te ha aparecido el siguiente mensaje de error:

Console
Hooks can only be called inside the body of a function component.
(Traducción)
Hooks sólo pueden ser llamados dentro del cuerpo de un componente de función.

Este mensaje puede aparecer por tres motivos comunes:

  1. Estás incumpliendo las Reglas de los Hooks.
  2. Estás usando versiones incompatibles de React y React DOM.
  3. Estás usando más de una instancia de React en la misma aplicación.

Veamos cada uno de estos casos.

Incumplir las Reglas de los Hooks

Las funciones que comienzan con use se conocen como Hooks en React.

Evita utilizar Hooks dentro de ciclos, condicionales o funciones anidadas. En su lugar, utiliza los Hooks únicamente en el nivel superior de tu función de React, antes de cualquier devolución anticipada. Los Hooks sólo deben ser utilizados durante la renderización de un componente de función en React:

function Counter() {
// ✅ Correcto: en la parte superior de un componente de función
const [count, setCount] = useState(0);
// ...
}

function useWindowWidth() {
// ✅ Correcto: en la parte superior de un Hook personalizado
const [width, setWidth] = useState(window.innerWidth);
// ...
}

No se permite el uso de Hooks (funciones que comienzan con use) en ningún otro caso, por ejemplo:

  • 🔴 Dentro de condicionales o ciclos.
  • 🔴 Después de una declaración de return condicional.
  • 🔴 En controladores de eventos.
  • 🔴 En componentes de clase.
  • 🔴 Dentro de funciones pasadas a useMemo, useReducer, o useEffect.

Incumplir estas reglas podría provocar la aparición de este error.

function Bad({ cond }) {
if (cond) {
// 🔴 Incorrecto: dentro de una condicional (para solucionarlo, ¡colócalo fuera!)
const theme = useContext(ThemeContext);
}
// ...
}

function Bad() {
for (let i = 0; i < 10; i++) {
// 🔴 Incorrecto: dentro de un ciclo (para solucionarlo, ¡colócalo fuera!)
const theme = useContext(ThemeContext);
}
// ...
}

function Bad({ cond }) {
if (cond) {
return;
}
// 🔴 Incorrecto: después de una devolución condicional (para solucionarlo, ¡colócalo antes de la devolución!)
const theme = useContext(ThemeContext);
// ...
}

function Bad() {
function handleClick() {
// 🔴 Incorrecto: dentro de un controlador de evento (para solucionarlo, ¡colócalo fuera!)
const theme = useContext(ThemeContext);
}
// ...
}

function Bad() {
const style = useMemo(() => {
// 🔴 Incorrecto: dentro de useMemo (para solucionarlo, ¡colócalo fuera!)
const theme = useContext(ThemeContext);
return createStyle(theme);
});
// ...
}

class Bad extends React.Component {
render() {
// 🔴 Incorrecto: dentro de un componente de clase (para solucionarlo, ¡escribe un componente de función en lugar de uno de clase!)
useEffect(() => {})
// ...
}
}

Puedes usar el plugin de eslint-plugin-react-hooks para detectar estos errores.

Nota

Los Hooks personalizados pueden llamar a otros Hooks (ese es su propósito principal). Esto es posible porque los Hooks personalizados también deben ser llamados sólo durante la renderización de un componente de función.

Uso de versiones incompatibles de React y React DOM

Puede que estés usando versiones antiguas de react-dom (< 16.8.0) o react-native (< 0.59) que no sean compatibles con Hooks. Para verificar la versión que estás utilizando, puedes ejecutar npm ls react-dom o npm ls react-native en la carpeta de tu aplicación. Si encuentras más de una versión instalada, esto podría causar problemas (más información a continuación).

Uso de más de una instancia de React

Para que los Hooks funcionen correctamente, la importación de react en el código de tu aplicación debe apuntar al mismo módulo que la importación de react en el paquete react-dom.

Si estas importaciones de react apuntan a dos objetos de importación diferentes, verás esta advertencia. Esto puede ocurrir si por error tienes dos copias del paquete react.

Si usas Node para gestionar paquetes, puedes verificar esto en la carpeta de tu proyecto ejecutando:

Terminal
npm ls react

Si detectas más de una instancia de React, tendrás que investigar las causas y corregir tu árbol de dependencias. Es posible que alguna biblioteca que estés utilizando especifique incorrectamente react como una dependencia en lugar de una dependencia entre pares. Mientras se soluciona este problema en la biblioteca, una posible solución temporal es utilizar resoluciones de Yarn.

Para solucionar el problema, puedes intentar depurarlo agregando logs y reiniciando el servidor de desarrollo:

// Añade esto en node_modules/react-dom/index.js
window.React1 = require('react');

// Añade esto en el archivo de tu componente
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);

Si imprime false, es posible que tengas dos instancias de React y debas investigar las posibles causas. Este issue incluye algunas razones comunes encontradas por la comunidad.

Este problema también puede surgir al utilizar npm link u otro método equivalente, lo que hace que el empaquetador de módulos «vea» dos versiones de React — una en la carpeta de la aplicación y otra en la carpeta de la biblioteca. Si myapp y mylib son carpetas hermanas, una posible solución es ejecutar npm link ../myapp/node_modules/react desde mylib. De esta forma, la biblioteca utilizará la versión de React de la aplicación.

Nota

En general, React permite el uso de varias instancias intependientes en una misma página (por ejemplo, si una aplicación y un widget de terceros lo utilizan). Sin embargo, puede surgir un problema si require('react') se resuelve de manera diferente entre el componente y la instancia de react-dom utilizada para renderizarlo.

Otras causas

Si ninguna de estas soluciones funcionó, por favor coméntalo en este issue y trataremos de ayudarte. Intenta crear un pequeño ejemplo que reproduzca el problema — Es posible que descubras la causa del problema mientras intentas reproducirlo.