Refs
Acesse nós DOM, armazene valores mutáveis que persistem entre renderizações e expose handles de componentes filhos.
Busque em todas as páginas da documentação
Acesse nós DOM, armazene valores mutáveis que persistem entre renderizações e expose handles de componentes filhos.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida - pronto para copiar e colar.
// Ref DOM
const inputRef = useRef<HTMLInputElement>(null);
<input ref={inputRef} />
inputRef.current?.focus();
// Valor mutável (sem re-renderização na alteração)
const renderCount = useRef(0);
renderCount.current += 1;
// Callback de ref (React 19 - suporta limpeza)
<div ref={(node) => {
if (node) {
// configuração
const observer = new ResizeObserver(() => { /* ... */ });
observer.observe(node);
return () => observer.disconnect(); // limpeza
}
}} />
// Encaminhamento de ref (React 19 - ref é uma prop regular)
function Input({ ref, ...props }: { ref?: React.Ref<HTMLInputElement> } & React.ComponentPropsWithoutRef<"input">) {
return <input ref={ref} {...props} />;
}Quando usar isso: Você precisa interagir diretamente com o DOM (focar, medir, rolar), integrar com uma biblioteca não-React ou armazenar um valor que não deve disparar re-renderizações.
import { useRef } from "react";
function FocusButton() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<div>
<input ref={inputRef} placeholder="Clique no botão para me focar" />
<button onClick={() => inputRef.current?.focus()}>Focar</button>
</div>
);
}O padrão de ref mais simples: obter um nó DOM e chamar um método nele. inputRef.current é null até que o <input> seja montado, então use encadeamento opcional (?.).
import { useRef } from "react";
function ScrollToSection() {
const sectionRef = useRef<HTMLDivElement>(null);
return (
<div>
<button onClick={() => sectionRef.current?.scrollIntoView({ behavior: "smooth" })}>
Ir para a seção
</button>
{/* ... muito conteúdo ... */}
<div ref={sectionRef}>
<h2>Seção Alvo</h2>
<p>Você rolou até aqui!</p>
</div>
</div>
);
}Refs permitem que você chame qualquer API DOM - scrollIntoView, getBoundingClientRect, play() em um vídeo, etc.
import { useRef } from "react";
function ClickTracker() {
const clickCount = useRef(0);
function handleClick() {
clickCount.current += 1;
console.log(`Clicado ${clickCount.current} vezes`);
}
return <button onClick={handleClick}>Clique em mim (verifique o console)</button>;
}Diferente de useState, atualizar ref.current não dispara uma re-renderização. Isso torna as refs ideais para rastrear valores que não precisam aparecer na UI - contadores, flags, computações em cache.
import { useRef } from "react";
function UncontrolledForm() {
const nameRef = useRef<HTMLInputElement>(null);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
alert(`Olá, ${nameRef.current?.value}`);
}
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} defaultValue="Mundo" />
<button type="submit">Cumprimentar</button>
</form>
);
}Em vez de configurar onChange + useState, você pode ler o valor do input diretamente do DOM via ref.current.value. Isso é chamado de input "não controlado".
import { useRef, useState } from "react";
function VideoPlayer({ src }: { src: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
const [playing, setPlaying] = useState(false);
function togglePlay() {
if (playing) {
videoRef.current?.pause();
} else {
videoRef.current?.play();
}
setPlaying(!playing);
}
return (
<div>
<video ref={videoRef} src={src} width={320} />
<button onClick={togglePlay}>{playing ? "Pausar" : "Reproduzir"}</button>
</div>
);
}Alguns elementos DOM têm APIs imperativas (play, pause, showModal, close) que só podem ser chamadas via ref - não há um equivalente de prop JSX.
"use client";
import { useRef, useState, useEffect } from "react";
export function AutoFocusSearch() {
const inputRef = useRef<HTMLInputElement>(null);
const [query, setQuery] = useState("");
const [results, setResults] = useState<string[]>([]);
const allItems = ["Apple", "Avocado", "Banana", "Blueberry", "Cherry", "Date", "Fig", "Grape"];
// Foca o input ao montar
useEffect(() => {
inputRef.current?.focus();
}, []);
useEffect(() => {
if (query.trim()) {
setResults(
allItems.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
)
);
} else {
setResults([]);
}
}, [query]);
return (
<div className="max-w-sm space-y-2 rounded border p-4">
<div className="flex gap-2">
<input
ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Pesquisar frutas..."
className="flex-1 rounded border px-3 py-1"
/>
<button
onClick={() => {
setQuery("");
inputRef.current?.focus();
}}
className="rounded bg-gray-200 px-3 py-1 text-sm"
>
Limpar
</button>
</div>
{results.length > 0 && (
<ul className="list-inside list-disc text-sm">
{results.map(item => (
<li key={item}>{item}</li>
))}
</ul>
)}
{query && results.length === 0 && (
<p className="text-sm text-gray-400">Nenhum resultado para "{query}"</p>
)}
</div>
);
}
// --- Callback de Ref com Limpeza (React 19) ---
export function MeasuredBox() {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const measureRef = (node: HTMLDivElement | null) => {
if (!node) return;
const observer = new ResizeObserver(([entry]) => {
const { width, height } = entry.contentRect;
setDimensions({ width: Math.round(width), height: Math.round(height) });
});
observer.observe(node);
// React 19: retorna uma função de limpeza
return () => observer.disconnect();
};
return (
<div className="space-y-2">
<div
ref={measureRef}
className="resize overflow-auto rounded border bg-blue-50 p-4"
style={{ minWidth: 100, minHeight: 60 }}
>
Redimensione-me!
</div>
<p className="text-xs text-gray-500">
{dimensions.width} x {dimensions.height}px
</p>
</div>
);
}O que isso demonstra:
useRef para obter um handle em um elemento <input> para foco programáticoinputRef.current?.focus()ResizeObserveruseRef(initialValue) retorna um objeto mutável { current: initialValue } que persiste por toda a vida útil do componenteref de um elemento JSX, o React define .current para o nó DOM após a montagem e de volta para null ao desmontar.current não dispara uma re-renderização - esta é a principal diferença do useStateuseEffect), que é executada quando o elemento é desmontado ou a ref mudaref é uma prop regular - não há necessidade de wrapper forwardRefuseRef:
| Parâmetro | Tipo | Descrição |
|---|---|---|
initialValue | T | Valor inicial para .current |
| Retorno | Tipo | Descrição |
|---|---|---|
ref | React.MutableRefObject<T> | Objeto com uma propriedade .current mutável |
| Tipo | Criado Por | Caso de Uso |
|---|---|---|
React.RefObject<T> | useRef<T>(null) | Referências a elementos DOM |
React.MutableRefObject<T> | useRef<T>(value) | Variáveis de instância mutáveis (timers, valores anteriores) |
React.Ref<T> | Callback ou objeto | Aceitando refs como props (união de callback ref e objeto ref) |
Armazenando o valor anterior:
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T | undefined>(undefined);
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Uso
const prevCount = usePrevious(count);Ref de timer (evitando closures obsoletos):
function Stopwatch() {
const [elapsed, setElapsed] = useState(0);
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
function start() {
if (intervalRef.current) return;
intervalRef.current = setInterval(() => {
setElapsed(prev => prev + 1);
}, 1000);
}
function stop() {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
useEffect(() => {
return () => stop(); // limpeza ao desmontar
}, []);
return (
<div>
<span>{elapsed}s</span>
<button onClick={start}>Iniciar</button>
<button onClick={stop}>Parar</button>
</div>
);
}Encaminhamento de refs no React 19 (não precisa de forwardRef):
interface FancyInputProps {
label: string;
ref?: React.Ref<HTMLInputElement>;
}
function FancyInput({ label, ref, ...props }: FancyInputProps) {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}
// Pai
function Form() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<FancyInput label="Nome" ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focar</button>
</>
);
}useImperativeHandle (expondo uma API customizada):
import { useRef, useImperativeHandle } from "react";
interface ModalHandle {
open: () => void;
close: () => void;
}
function Modal({ ref, children }: { ref?: React.Ref<ModalHandle>; children: React.ReactNode }) {
const dialogRef = useRef<HTMLDialogElement>(null);
useImperativeHandle(ref, () => ({
open: () => dialogRef.current?.showModal(),
close: () => dialogRef.current?.close(),
}));
return <dialog ref={dialogRef}>{children}</dialog>;
}
// Pai
function App() {
const modalRef = useRef<ModalHandle>(null);
return (
<>
<button onClick={() => modalRef.current?.open()}>Abrir</button>
<Modal ref={modalRef}>
<p>Olá!</p>
<button onClick={() => modalRef.current?.close()}>Fechar</button>
</Modal>
</>
);
}// Ref DOM - passe null como valor inicial, tipando o elemento
const divRef = useRef<HTMLDivElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
// Ref mutável - valor inicial não nulo
const countRef = useRef<number>(0); // MutableRefObject<number>
// Aceitando uma prop ref no React 19
interface Props {
ref?: React.Ref<HTMLInputElement>;
}
// Tipando useImperativeHandle
interface Handle {
scrollToTop: () => void;
}
useImperativeHandle(ref, (): Handle => ({
scrollToTop: () => window.scrollTo(0, 0),
}));Ler ref durante a renderização - inputRef.current é null durante a primeira renderização porque o nó DOM ainda não existe. Correção: Acesse refs em manipuladores de eventos, useEffect, ou após uma verificação de nulo.
Alterações de ref não disparam re-renderizações - Atualizar ref.current não fará com que o componente re-renderize ou reflita as alterações no JSX. Correção: Se você precisa que a UI atualize, use useState em vez disso. Use refs apenas para valores dos quais a saída de renderização não depende.
forwardRef está obsoleto no React 19 - React.forwardRef ainda funciona, mas não é mais necessário. Correção: Aceite ref como uma prop regular: function MyComp({ ref }: { ref?: React.Ref<HTMLElement> }).
Atribuir ref a um elemento condicional - Se o elemento com ref for renderizado condicionalmente, .current será null quando o elemento estiver oculto. Correção: Sempre verifique se não é nulo antes de usar: ref.current?.focus().
Callback de ref disparando duas vezes no StrictMode - Em desenvolvimento com StrictMode, os callbacks de ref disparam com null e depois com o nó, simulando montagem/desmontagem/remontagem. Correção: Isso é esperado - certifique-se de que seu callback de ref lida com null graciosamente. No React 19, retorne uma função de limpeza em vez de verificar null.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
useState | O valor precisa disparar re-renderizações quando muda | Você está armazenando um ID de timer, valor anterior ou nó DOM |
document.getElementById | Protótipo rápido fora do React | Código de produção React (quebra a encapsulação do componente) |
| Atributos de dados + CSS | Você precisa alternar estilos com base no estado | Você precisa de acesso programático ao DOM (foco, rolagem, medição) |
useImperativeHandle | Você quer expor uma API limitada de um componente filho | Você só precisa do nó DOM bruto |
useRef retorna um objeto mutável { current: value } que persiste entre renderizações sem causar re-renderizações. Use-o para acesso DOM (foco, rolagem, medição), armazenamento de IDs de timer e manutenção de valores mutáveis dos quais a UI não depende.
useState dispara uma re-renderização quando atualizado - use para valores que a UI exibeuseRef não dispara re-renderizações - use para valores como IDs de timer, valores anteriores ou nós DOMRefs DOM são preenchidas após o React montar o elemento. Durante a renderização, o nó DOM ainda não existe. Acesse refs em manipuladores de eventos, useEffect, ou atrás de uma verificação de nulo: ref.current?.focus().
Não. No React 19, ref é uma prop regular. Aceite-a diretamente:
function Input({ ref, ...props }: { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />;
}Um callback de ref é uma função passada para a prop ref. O React a chama com o nó DOM na montagem e null na desmontagem. No React 19, ela pode retornar uma função de limpeza. Use-a quando precisar de lógica de configuração/limpeza ligada ao nó DOM (como ResizeObserver).
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
timerRef.current = setInterval(() => { ... }, 1000);
// Mais tarde:
clearInterval(timerRef.current!);Isso evita problemas de closure obsoleto, pois ref.current sempre aponta para o valor mais recente.
Ele permite que você personalize o valor exposto quando um componente pai usa uma ref no seu componente. Em vez de expor o nó DOM bruto, você expõe uma API limitada como { open(), close() }. Use com moderação - prefira props para a maioria das comunicações.
Sim. Incremente ref.current no corpo do componente:
const renderCount = useRef(0);
renderCount.current += 1;Como as alterações de ref não disparam re-renderizações, isso não causará um loop infinito.
O StrictMode do React simula desmontagem/remontagem em desenvolvimento, então o callback de ref dispara com null e depois com o nó. Isso é esperado. No React 19, retorne uma função de limpeza do callback de ref em vez de verificar null.
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T | undefined>(undefined);
useEffect(() => { ref.current = value; }, [value]);
return ref.current;
}A ref armazena o valor antigo porque useEffect é executado após a renderização.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥