Saltar al contenido

Las reglas de los hooks son consecuencias

¿Y si llamo un hook dentro de un if? ¿Qué se rompe exactamente?

Al terminar vas a poder explicar

  • Derivar las dos reglas desde la estructura de datos, sin memorizarlas
  • Leer y entender el error real que tira React
  • Reescribir un componente con hooks condicionales sin romper la cadena

Las dos reglas las sabés de memoria:

  1. Sólo llamá hooks en el nivel superior. Nunca dentro de condicionales, bucles o funciones anidadas.
  2. Sólo llamá hooks desde componentes de React o desde otros hooks.

Y probablemente las cumplís porque ESLint te grita. Está bien — pero cumplir una regla que no entendés te deja indefenso el día que el linter no la detecta.

Después de la lección anterior tenés todo lo necesario para derivarlas. Vamos.

Mirá la cadena romperse

Este componente llama un hook adentro de un if. Monta perfecto, porque en el primer render la condición es falsa. Al segundo, se rompe.

La cadena de hooks rompiéndose en vivo

AGENDADOpaso 1 / 48
Traza
Cadena de hooks · <>

Sin hooks en este paso.

Árbol de fibers

  • <HostRoot>

Salida

Nada en el host todavía. React no commiteó ningún cambio.

createRoot().render()

Montaje inicial. No hay árbol previo, así que todo se va a crear desde cero.

La corrida se detuvo: Se renderizaron MÁS hooks que en el render anterior en <Anónimo>. React recorre la cadena por posición; si aparece un hook que antes no estaba, se queda sin nodo al que apuntar.

VER EL CÓDIGO QUE SE ESTÁ TRAZANDO
1function HookCondicional() {2  const [n, setN] = useState(0);3 4  if (n > 0) {5    useState("este hook aparece tarde");  // ⚠️ rompe la cadena6  }7 8  return <button onClick={() => setN(1)}>n = {n}</button>;9}

Con el foco acá: ← → un paso · espacio reproduce · Inicio/Fin van a los extremos.

Andá al final de la traza. React no encontró un nodo en la posición donde tu código pidió un hook, y no tiene forma de inventar uno: no sabe qué estado debería tener, ni de dónde sacarlo. Lo único honesto que puede hacer es fallar fuerte y ahí mismo.

El caso silencioso, que es peor

Si la cantidad de hooks cambia, React tira un error y te enterás. Pero si cambia el orden sin cambiar la cantidad, el error puede ser mucho más sutil:

1function Perfil({ esAdmin }) {2  if (esAdmin) {3    const [permisos, setPermisos] = useState([]);4    const [log, setLog] = useState("");5  } else {6    const [tema, setTema] = useState("oscuro");7    const [idioma, setIdioma] = useState("es");8  }9  // dos hooks en las dos ramas… pero NO son los mismos dos10}

Dos hooks en las dos ramas. La cadena tiene el mismo largo, así que el chequeo de cantidad pasa. Pero si esAdmin cambia, el hook de la posición [0] pasa de guardar un array de permisos a guardar la cadena "oscuro". Sin error. Con datos corruptos.

React sólo compara posiciones. No compara qué hook es. En el trazador vas a ver que sí detectamos el cambio de tipo — porque la traza guarda el kind de cada hook — pero React en producción no tiene esa información y no te avisa.

La forma correcta

Llamá siempre todos los hooks, y poné la condición adentro o en el uso del resultado:

1function Lista({ items }) {2  const [abierto, setAbierto] = useState(false);3  const [filtro, setFiltro] = useState("");     // siempre se llama4 5  useEffect(() => {6    if (!abierto) return;                        // la condición va ADENTRO7    suscribirse(filtro);8  }, [abierto, filtro]);9 10  if (items.length === 0) return <Vacio />;      // el return va al final11  return <div>…</div>;12}

Y si un bloque de estado sólo tiene sentido en una rama, no lo pelees: sacalo a un componente aparte. Un componente que se monta y desmonta tiene su propia cadena de hooks, limpia, y ese montaje condicional es completamente legal.

La segunda regla

Los hooks se guardan en currentlyRenderingFiber.memoizedState, una variable de módulo que React setea justo antes de llamar tu función y limpia apenas termina. Fuera de un render, esa variable es null.

Por eso llamar un hook desde un manejador de eventos, desde un setTimeout o desde una función suelta no «está prohibido»: es que no hay dónde guardar el estado. El fiber destino no existe en ese momento.

Antes de seguir · predecí

¿Cuál de estos códigos NO viola las reglas de los hooks?

Lo que te llevás

Las reglas de los hooks no son convenciones: son la consecuencia directa de que los hooks vivan en una lista enlazada indexada por orden de llamada. Ahora que viste la cadena romperse, no te hacen falta las reglas — te alcanza con la estructura.

Antes de marcarla, comprobá

  • ¿Podés explicar derivar las dos reglas desde la estructura de datos, sin memorizarlas?
  • ¿Podés explicar leer y entender el error real que tira React?
  • ¿Podés explicar reescribir un componente con hooks condicionales sin romper la cadena?