Tipado de Refs
Receta
Tipea correctamente useRef para referencias de elementos DOM y contenedores de valores mutables. Entiende la diferencia entre refs de solo lectura del DOM y refs mutables, y cuándo usar cada una.
Ejemplo funcionando
// Ref de elemento DOM (current de solo lectura)
function TextInput() {
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Enfocar entrada</button>
</div>
);
}// Ref mutable (current escribible)
function Timer() {
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const [count, setCount] = useState(0);
const start = () => {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setCount((c) => c + 1);
}, 1000);
};
const stop = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
return (
<div>
<p>{count}</p>
<button onClick={start}>Iniciar</button>
<button onClick={stop}>Detener</button>
</div>
);
}Análisis profundo
Cómo funciona
useRef<HTMLInputElement>(null)crea un objeto ref con{ current: HTMLInputElement | null }. Cuando se pasa a un atributo JSXref, React administra el establecimiento de.currental nodo del DOM.- En React 19, ref es una prop normal en lugar de un atributo especial. Los componentes de función pueden aceptar
refdirectamente en sus props sinforwardRef. - Para refs mutables (almacenar temporizadores, valores anteriores, etc.), inicializa con
nully tipea en consecuencia:useRef<NodeJS.Timeout | null>(null). - La distinción entre refs de DOM y refs mutables es conceptual. Ambas usan
useRef. La diferencia es si React administra.current(ref de DOM) o tú lo administras (ref mutable).
Variaciones
Almacenar valor anterior:
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T | undefined>(undefined);
useEffect(() => {
ref.current = value;
});
return ref.current;
}Patrón de callback de ref:
function MeasuredBox() {
const [height, setHeight] = useState(0);
const measuredRef = (node: HTMLDivElement | null) => {
if (node) {
setHeight(node.getBoundingClientRect().height);
}
};
return (
<div ref={measuredRef}>
<p>Este cuadro tiene {height}px de alto</p>
</div>
);
}Ref de elemento de video:
const videoRef = useRef<HTMLVideoElement>(null);
// Reproducir/pausar
const togglePlay = () => {
if (videoRef.current?.paused) {
videoRef.current.play();
} else {
videoRef.current?.pause();
}
};Notas de TypeScript
- Tipos comunes de ref de DOM:
HTMLDivElement,HTMLInputElement,HTMLButtonElement,HTMLFormElement,HTMLCanvasElement,HTMLVideoElement,SVGSVGElement. ReturnType<typeof setInterval>es portátil entre entornos Node y navegador en lugar de codificarNodeJS.Timeoutonumber.- En React 19,
forwardRefya no es necesario. Los componentes pueden aceptarrefcomo una prop normal:function Input({ ref }: { ref?: React.Ref<HTMLInputElement> }).
Trampa
- Acceder a
inputRef.currentsin una verificación de null generará un error enstrictNullChecks. Siempre usa encadenamiento opcional (?.) o una guarda. - Asignar a
.currenten una ref de DOM administrada por React no tiene efecto. React lo sobrescribe durante el renderizado. - Usar
useRef<HTMLInputElement>(null!)(aserción de no-null) elimina la necesidad de verificaciones de null pero oculta errores potenciales si la ref se accede antes del montaje. - Los callbacks de ref se disparan con
nullcuando el componente se desmonta. Siempre maneja el casonull.
Alternativas
| Enfoque | Pros | Contras |
|---|---|---|
useRef<T>(null) | Seguro, manejo explícito de null | Requiere verificaciones de null en todas partes |
useRef<T>(null!) | Sin verificaciones de null necesarias | No seguro si se accede antes del montaje |
| Función de callback de ref | Se ejecuta en montaje/desmontaje, útil para mediciones | Más complejo, se dispara en cada renderizado en React 18 |
React.createRef() | Compatible con componentes de clase | Crea una ref nueva cada renderizado en componentes de función |
ref como prop (React 19) | No se necesita envoltorio forwardRef | Solo disponible en React 19+ |
Preguntas frecuentes
¿Cuál es la diferencia entre una ref de DOM y una ref mutable?
- Una ref de DOM se pasa a un atributo JSX
refy React administra.current(estableciéndola al nodo del DOM). - Una ref mutable es aquella donde administras
.currenttú mismo (p. ej., almacenando una ID de temporizador o un valor anterior). - Ambas usan
useRef. La diferencia es quién escribe en.current.
¿Por qué necesito pasar null como valor inicial para una ref de DOM?
- Las refs de DOM comienzan como
nullporque el nodo del DOM no existe hasta que el componente se monta. useRef<HTMLInputElement>(null)proporciona el tipoHTMLInputElement | null, que coincide con este ciclo de vida.
¿Cómo tipeo una ref para un elemento de video?
const videoRef = useRef<HTMLVideoElement>(null);- Usa
HTMLVideoElementcomo el parámetro de tipo. - Accede a métodos como
videoRef.current?.play()con encadenamiento opcional.
¿Qué hace ReturnType<typeof setInterval> y por qué usarlo?
- Extrae el tipo de retorno de
setInterval, que difiere entre Node (NodeJS.Timeout) y navegadores (number). - Usar
ReturnType<typeof setInterval>es portátil entre ambos entornos sin codificar.
¿Qué es un callback de ref y cuándo debo usar uno?
- Un callback de ref es una función pasada al atributo
ref:ref={(node) => { ... }}. - Se dispara cuando el elemento se monta (con el nodo) y se desmonta (con
null). - Úsalo cuando necesites medir u observar un elemento en el montaje.
¿Todavía necesito forwardRef en React 19?
- No. En React 19,
refes una prop normal. Los componentes pueden aceptarla directamente:
function Input({ ref }: { ref?: React.Ref<HTMLInputElement> }) { ... }forwardReftodavía funciona pero ya no es necesario.
Trampa: ¿Qué sucede si accedo a una ref de DOM antes de que el componente se monte?
inputRef.currentseránullantes del montaje.- Si usas
useRef<HTMLInputElement>(null!)para omitir verificaciones de null, ocultas este error. - Siempre usa encadenamiento opcional (
?.) o una guarda de null en código de producción.
Trampa: ¿Puedo asignar manualmente .current en una ref de DOM administrada por React?
- Puedes asignarla, pero React sobrescribe
.currentdurante el renderizado. - Tu asignación se perderá en el siguiente renderizado. No confíes en asignación manual para refs de DOM.
¿Cómo funciona el hook personalizado usePrevious con refs?
- Almacena el valor anterior en una ref mutable:
useRef<T | undefined>(undefined). - Un
useEffectactualizaref.currentdespués de cada renderizado, por lo que el valor devuelto siempre está un renderizado atrás.
¿Qué tipo de TypeScript debo usar para un callback de ref?
- El tipo de parámetro es el tipo de elemento o
null:(node: HTMLDivElement | null) => void. - Siempre maneja el caso
nullya que el callback se dispara connullen el desmontaje.
¿Cuáles son los tipos de ref de DOM más comunes en TypeScript?
HTMLDivElement,HTMLInputElement,HTMLButtonElement,HTMLFormElementHTMLCanvasElement,HTMLVideoElement,SVGSVGElement
¿Cuándo se dispara un callback de ref con null?
- Cuando el componente se desmonta, React llama al callback de ref con
null. - En React 18, los callbacks de ref también se disparan en cada re-renderizado. En React 19, el comportamiento de limpieza se mejora.