React TypeError: Cannot read properties of null (reading 'useRef')

Imagen principal del artículo

Este error explota cuando se llama a un Hook de React fuera del contexto de un componente funcional, o cuando el módulo de React no está correctamente resuelto a una única instancia. El runtime trata React como null y no puede acceder a su propiedad useRef.

El Problema

// ❌ Bad Code — lib/focusHelper.js
// Se importa y se llama useRef FUERA de un componente React
import { useRef } from 'react';

// Función utilitaria normal, NO un componente ni un hook custom
export function createFocusRef() {
  const ref = useRef(null); // ← Rompe las Reglas de los Hooks
  return ref;
}

// En el componente:
import { createFocusRef } from './focusHelper';

export default function InputField() {
  const inputRef = createFocusRef(); // TypeError aquí
  return <input ref={inputRef} />;
}

Error en consola:

TypeError: Cannot read properties of null (reading 'useRef')
    at Object.useRef (react.development.js:1571)
    at createFocusRef (focusHelper.js:5)

La Solución

// ✅ Good Code — lib/focusHelper.js
// La lógica auxiliar solo acepta una ref ya creada, no la crea ella misma
export function focusElement(ref) {
  if (ref?.current) {
    ref.current.focus();
  }
}

// En el componente — useRef se llama DENTRO del componente
import { useRef } from 'react';
import { focusElement } from './focusHelper';

export default function InputField() {
  const inputRef = useRef(null); // ✅ Dentro del componente

  return (
    <input
      ref={inputRef}
      onFocus={() => focusElement(inputRef)}
    />
  );
}

Por qué funciona

Las Reglas de los Hooks de React exigen que useRef (y cualquier otro Hook) se invoque únicamente en el nivel superior de un componente funcional o de un Hook personalizado (useXxx). Cuando se llama desde una función normal, React pierde el contexto de la fibra actual y devuelve null como dispatcher, provocando el TypeError. La solución es mover la creación de la ref al cuerpo del componente y pasar la ref ya instanciada a las funciones helper que la necesiten. Si la lógica es compleja y reutilizable, la alternativa correcta es convertirla en un custom Hook (useFocusRef) que sí puede contener Hooks internamente.