React: useRef e manipulação do DOM
Última atualização: 2026-10-26
useRefÉ como um cofre — você pode guardar qualquer coisa lá dentro, e ela ainda estará lá quando você a retirar. Ao contrário douseState, alterar o valor dorefnão fará com que o componente seja renderizado novamente.
1. O que você vai aprender
- Noções básicas sobre o uso do
useRef(Referências DOM) - O useRef armazena valores mutáveis (sem acionar uma nova renderização)
- forwardRef: Passa uma referência a um componente filho
- useImperativeHandle expõe os métodos do componente filho
- Escolhendo entre useRef e useState
2. A história de uma barra de pesquisa
Os dois principais usos do useRef
flowchart LR
subgraph "Uses1: DOMQuote"
A[useRef] --> B["ref={inputRef}"]
B --> C["inputRef.current.focus()"]
end
subgraph "Uses2: Variable-Value Storage"
D[useRef] --> E[".current = value]
E --> F[Does not trigger a repaint]
end
G[forwardRef] --> H[Passed from the parent componentref]
H --> I[Child Component ExposureDOM]
style A fill:#e3f2fd,stroke:#1565c0
style D fill:#e8f5e9,stroke:#2e7d32
style G fill:#fff3e0,stroke:#e65100
(1) Problema: Para selecionar um campo de entrada, é necessário manipular o DOM
Bob criou uma página de busca e queria que a página focasse automaticamente no campo de busca ao carregar:
▶ ExperimenteBob quer usar useState para implementar:
(2) Uma solução utilizando useRef
▶ Experimente
Saída:
Botões: Select All Text, Scroll to the bottom, Measure the height. Entrada: campo de entrada com foco automático. Efeitos colaterais via useEffect. Referência ao DOM via useRef
Benefícios: useRef retorna um objeto mutável { current: null }, e .current é um elemento DOM real. Não são necessários seletores nem getElementById — é naturalmente compatível com o React.
3. Duas principais formas de uso do useRef
| Finalidade | ref.current aponta para | Aciona uma atualização de renderização | Cenários típicos |
|---|---|---|---|
| Referência do DOM | Elemento do DOM | ❌ | Foco, rolagem, medição de dimensões |
| Armazenamento de variáveis | Qualquer valor em JavaScript | ❌ | ID do temporizador, valor anterior, estado fora de renderização |
(1) Caso de uso 1: Referências ao DOM
Os usos mais comuns — recuperar elementos do DOM, manipular o foco, o tamanho, a posição de rolagem e assim por diante.
| Operação | Código | Descrição |
|---|---|---|
| Foco | inputRef.current.focus() |
Campo de entrada do foco automático |
| Selecionar texto | inputRef.current.select() |
Selecionar tudo no campo de entrada |
| Ir até | divRef.current.scrollIntoView() |
Ir até um elemento específico |
| Ler dimensões | divRef.current.offsetHeight |
Obter a altura do elemento |
| Reproduzir vídeo | videoRef.current.play() |
Controlar a reprodução do vídeo |
▶ Exemplo: 5 operações comuns no DOM
▶ ExperimenteSaída:
Foco automático no campo de entrada, botão "Select All Text", lista rolável de 20 linhas (com "Scroll to bottom"), caixa com "Measure the height" → exibe o valor em px
(2) Caso de uso 2: Armazenamento de valores de variáveis (sem acionar uma atualização da tela)
Esse é o truque secreto do useRef — ele armazena qualquer valor, e o componente não será renderizado novamente quando esse valor mudar.
▶ Experimente4. useRef x useState
| Dimensão | useRef | useState |
|---|---|---|
| Valor de retorno | { current: initial } |
[value, setter] |
| Método de modificação | ref.current = newValue |
setState(newValue) |
| Renderização após modificação | ❌ Não aciona | ✅ Aciona uma nova renderização |
| Quando é lido | Lê o valor mais recente imediatamente | Não o lê até a próxima renderização |
| Casos de uso | Operações no DOM, IDs de temporizadores, valores anteriores | Dados a serem exibidos na interface do usuário |
Saída:
Exibe: "DOM Example of Operation". Botões: Select All Text, Scroll to the bottom, Measure the height. Entrada: campo de entrada com foco automático. useEffect gerencia efeitos colaterais. useRef referencia o DOM/variável
▶ Exemplo: Usando useRef para salvar o valor anterior
▶ Experimente
Saída:
Atual: 0, anterior: (first time). Clique em +1 → Atual: 1, anterior: 0. Cada clique mostra o valor atual e o anterior.
5. forwardRef: Componentes pais controlam o DOM dos componentes filhos
Por padrão, os componentes de função não expõem suas próprias referências ao DOM. O uso de forwardRef permite que os componentes filhos recebam o ref do componente pai:
6. useImperativeHandle: Expor um método específico
Se você não quiser expor todo o elemento DOM, use useImperativeHandle para expor apenas métodos específicos:
7. Exemplo completo: Editor de texto rico
▶ Expandir exemplo executável (107 linhas)
Saída:
Estado: count (setter: setCount). Botão: setCount(c => c + 1)}>+1. useEffect gerencia efeitos colaterais. useRef referencia o DOM/variável
Resultado esperado: Um editor de texto rico que ofereça suporte a negrito, itálico, sublinhado, listas, títulos H2 e outras opções de formatação; o conteúdo pode ser salvo, carregado ou apagado externamente por meio de um
ref.
▶ Exemplo 3: Como usar useRef para fechar uma janela pop-up ao clicar nela de fora
▶ Experimente
Saída:
Botão "Menu ▾" → menu suspenso (Profile, Settings, Logout). Clique fora → fecha automaticamente via hook useClickOutside.
Saída:
Botão com throttling: cliques rápidos são ignorados (no máximo 1 por segundo). Contador de cliques + feedback "Throttled!". useRef armazena o timestamp de lastClick
▶ Exemplo 4: Usando useRef para armazenar um temporizador na implementação de um botão com limitação de frequência
▶ Experimente
Saída:
Clique → "Clicked!" + contador. Clicar novamente dentro de 1s → "Too fast! Wait 1 second." (vermelho). Throttling via timestamp no useRef.
Saída:
Exibe: "Click Me". Estado: clicks (setter: setClicks), feedback (setter: setFeedback). Botão: Click Me. useRef referencia o DOM/variável. Comportamento baseado em temporizador
▶ Exemplo 5: Usando o forwardRef para envolver um componente Input que pode receber foco
const FancyInput = React.forwardRef(function FancyInput({ label, error, ...props }, ref) {
const internalRef = useRef(null)
useImperativeHandle(ref, () => ({
focus: () => internalRef.current?.focus(),
selectAll: () => {
const el = internalRef.current
if (el) { el.focus(); el.select() }
},
scrollIntoView: () => internalRef.current?.scrollIntoView({ behavior: 'smooth' }),
}))
return (
<div style={{ marginBottom: 12 }}>
<label style={{ display: 'block', marginBottom: 4, fontSize: 14, fontWeight: 500 }}>{label}</label>
<input
ref={internalRef}
style={{ width: '100%', padding: 8, borderRadius: 4, border: `1px solid ${error ? '#ff4d4f' : '#d9d9d9'}`, outline: 'none' }}
{...props}
/>
{error && <p style={{ color: '#ff4d4f', fontSize: 12, margin: '4px 0 0' }}>{error}</p>}
</div>
)
})
function FormWithFancyInput() {
const emailRef = useRef(null)
function handleSubmit(e) {
e.preventDefault()
emailRef.current?.focus()
}
return (
<form onSubmit={handleSubmit} style={{ maxWidth: 400, margin: '0 auto' }}>
<h3>Sign Up</h3>
<FancyInput ref={emailRef} label="Email" type="email" placeholder="you@example.com" />
<FancyInput label="Password" type="password" placeholder="At least 6 characters" />
<button type="submit" style={{ padding: '8px 24px', cursor: 'pointer' }}>Submit</button>
</form>
)
}
Saída:
Formulário de cadastro: campos de Email e Password com rótulos e estilo de erro. Ao enviar, o foco vai para email. Expõe os métodos focus(), selectAll(), scrollIntoView().
❓ Perguntas Frequentes
forwardRef em todos os componentes?props.useRef pode substituir o useState?useRef não acionam uma nova renderização do componente, ao passo que o setState sim. Se o seu valor precisar ser exibido na interface do usuário ou afetar o resultado da renderização, você deve usar o useState. Se você estiver simplesmente armazenando “dados que não precisam ser mostrados ao usuário” (como um ID de intervalo, o valor da renderização anterior ou uma instância de WebSocket), use useRef. Pense em useRef como um “contêiner de variáveis que não aciona uma nova renderização”.📖 Resumo
useRefretorna a{ current: initialValue }; alterar.currentnão aciona uma nova renderização- Duas finalidades: ① Referências DOM (foco, tamanho, rolagem) ② Armazenamento de valores de variáveis (ID do temporizador, valor anterior)
forwardRef: Permitir que um componente pai controle o DOM de um componente filho por meio de uma refuseImperativeHandle: Um método para limitar a exposição, fornecendo apenas as APIs necessárias ao componente pai- Sempre verifique se há valores nulos antes de realizar operações com a referência:
ref.current?.focus()
📝 Exercícios
- Exercício básico (Dificuldade ⭐): Crie um componente
AutoFocusInputque receba foco automaticamente ao ser montado e forneça dois métodos —focus()eclear()— para que o componente pai os chame. - Exercício avançado (Dificuldade ⭐⭐): Crie um componente
ClickCounter, useuseRefpara rastrear o número de cliques (sem exibi-lo na interface do usuário) e useuseEffectpara registrar “Clicado X vezes” no console a cada renderização. - Desafio (Dificuldade: ⭐⭐⭐): Crie um componente
InfiniteScrollque utilizeuseRefpara detectar quando o elemento sentinela (um elemento específico na parte inferior da página) entrar na área de visualização, acionando o carregamento de mais dados. Utilize a APIIntersectionObserver.