Noções Básicas de Eventos do React
11 exemplos para você começar com Eventos do React -- 7 básicos e 4 intermediários.
Busque em todas as páginas da documentação
11 exemplos para você começar com Eventos do React -- 7 básicos e 4 intermediários.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Nenhum pacote extra é necessário -- cada manipulador nesta página vem com o React. Um projeto React padrão (Next.js, Vite ou CRA) é suficiente.
Duas convenções se aplicam a todos os exemplos:
onClick, onChange, onKeyDown -- não minúsculas.Event nativo (e.target, e.preventDefault(), etc.).Os manipuladores de eventos são executados no cliente. Em um projeto Next.js App Router, coloque-os dentro de um componente marcado como "use client" (ou importe esse componente de um Server Component).
Responda ao pressionamento de um botão com um manipulador de clique.
"use client";
export default function ClickCounter() {
const handleClick = () => {
alert("Botão clicado!");
};
return <button onClick={handleClick}>Clique em mim</button>;
}onClick={handleClick}), não uma chamada de função (onClick={handleClick()}).React.MouseEvent com clientX, clientY, shiftKey, etc.onClick pai também dispara, a menos que um filho chame e.stopPropagation().<button type="button"> dentro de um <form> a menos que você realmente queira que ele envie.Relacionado: Eventos de Mouse -- clique, hover, arrastar, menu de contexto | Eventos (Fundamentos) -- visão geral de eventos sintéticos | Tipagem de Eventos --
MouseEvent<HTMLButtonElement>e similares
Capture pressionamentos de tecla com um input controlado.
"use client";
import { useState } from "react";
export default function NameInput() {
const [name, setName] = useState("");
return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Seu nome"
/>
<p>Olá, {name || "estranho"}!</p>
</div>
);
}onChange dispara a cada pressionamento de tecla -- ao contrário do change nativo do DOM, que dispara apenas no blur.e.target.value é sempre uma string; analise números/booleanos explicitamente.value no estado do React -- você tem controle total sobre validação e transformação.react-hook-form para evitar um re-render por pressionamento de tecla.Relacionado: Eventos de Formulário -- onChange, onInput, onSubmit | Formulários (Fundamentos) -- controlado vs. não controlado | React Hook Form -- escalando formulários além de alguns campos
Responda a pressionamentos de tecla, incluindo modificadores e combinações de teclas.
"use client";
import { useState } from "react";
export default function SearchBox() {
const [query, setQuery] = useState("");
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
console.log("Pesquisar por:", query);
} else if (e.key === "Escape") {
setQuery("");
}
};
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Pesquisar..."
/>
);
}e.key (por exemplo, "Enter", "Escape", "ArrowUp") -- não o e.keyCode obsoleto.e.ctrlKey, e.metaKey, e.shiftKey, e.altKey -- útil para atalhos.useEffect com window.addEventListener("keydown", ...).onKeyDown dispara antes que o caractere seja inserido; onKeyUp dispara após o soltar.Relacionado: Eventos de Teclado -- atalhos, composição IME, acessibilidade | useKeyboardShortcut -- hook reutilizável de atalhos globais
Acompanhe quando um elemento ganha ou perde o foco -- para validação, tooltips e estado da UI.
"use client";
import { useState } from "react";
export default function EmailField() {
const [email, setEmail] = useState("");
const [touched, setTouched] = useState(false);
const showError = touched && !email.includes("@");
return (
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onBlur={() => setTouched(true)}
/>
{showError && <p role="alert">Por favor, insira um email válido.</p>}
</div>
);
}onFocus dispara quando o elemento recebe foco; onBlur quando ele perde o foco.focus/blur nativo. Use-os em contêineres, não apenas em inputs.onBlur antes de mostrar erros de validação para que os usuários não sejam alertados no meio da digitação.relatedTarget nativo no evento.Relacionado: Eventos de Foco -- onFocus, onBlur, onFocusIn | Acessibilidade de Formulários -- padrões ARIA em torno do foco
Crie interações de hover sem as peculiaridades do mouseover nativo.
"use client";
import { useState } from "react";
export default function Tooltip() {
const [open, setOpen] = useState(false);
return (
<span
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
style={{ position: "relative" }}
>
Passe o mouse aqui
{open && (
<span style={{ position: "absolute", top: "-1.5rem", left: 0 }}>
Tooltip!
</span>
)}
</span>
);
}onMouseEnter/onMouseLeave disparam uma vez ao entrar/sair do elemento -- eles não disparam para filhos, ao contrário de onMouseOver/onMouseOut.onPointerEnter/onPointerLeave para entrada unificada.:hover para estilo puramente visual -- reserve manipuladores de JS para comportamento.Relacionado: Eventos de Mouse -- API completa de mouse | Eventos de Ponteiro -- mouse + toque + caneta unificados | Componente Tooltip -- tooltip pronto para produção
Manipule o envio de formulário sem uma recarga completa da página.
"use client";
import { useState, type FormEvent } from "react";
export default function ContactForm() {
const [email, setEmail] = useState("");
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log("Enviar:", email);
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Enviar</button>
</form>
);
}e.preventDefault() para interromper a recarga padrão da página do navegador.onSubmit ao <form>, não ao botão de envio -- ele dispara em cliques de botão e pressionamentos de Enter.<form action={serverAction}> em vez disso e pule onSubmit.type="submit" real para que usuários de teclado possam enviar com Enter.Relacionado: Eventos de Formulário -- onSubmit, onReset, onInvalid | Formulários de Ação do Servidor -- fluxo nativo de formulário do React 19
Intercepte operações da área de transferência para transformar ou bloquear o conteúdo.
"use client";
export default function ProtectedText() {
const handleCopy = (e: React.ClipboardEvent<HTMLDivElement>) => {
e.preventDefault();
e.clipboardData.setData("text/plain", "A cópia está desabilitada aqui.");
};
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
const pasted = e.clipboardData.getData("text");
console.log("Colado:", pasted);
};
return (
<div onCopy={handleCopy}>
<p>Tente copiar este texto.</p>
<input onPaste={handlePaste} placeholder="Cole aqui" />
</div>
);
}e.clipboardData expõe getData (para colar) e setData (para copiar/recortar).e.preventDefault() antes de setData para substituir o conteúdo padrão da área de transferência.navigator.clipboard.writeText() em vez disso.Relacionado: Eventos da Área de Transferência -- API completa de onCopy/onCut/onPaste | useCopyToClipboard -- hook reutilizável de botão "copiar"
Manipule mouse, toque e caneta com um único conjunto de manipuladores -- sem ramificação por tipo de entrada.
"use client";
import { useState } from "react";
export default function Draggable() {
const [pos, setPos] = useState({ x: 0, y: 0 });
const [dragging, setDragging] = useState(false);
const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
e.currentTarget.setPointerCapture(e.pointerId);
setDragging(true);
};
const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
if (dragging) setPos({ x: e.clientX, y: e.clientY });
};
const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
e.currentTarget.releasePointerCapture(e.pointerId);
setDragging(false);
};
return (
<div
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
style={{
position: "absolute",
left: pos.x,
top: pos.y,
width: 60,
height: 60,
background: dragging ? "crimson" : "steelblue",
touchAction: "none",
}}
/>
);
}setPointerCapture(pointerId) continua recebendo eventos pointermove mesmo que o cursor saia do elemento.touch-action: none em CSS para que o navegador não roube o gesto para rolagem/zoom.e.pointerType ("mouse", "touch", "pen") apenas quando a interação realmente precisar de ramificação.Relacionado: Eventos de Ponteiro -- captura, eventos coalescidos, pressão da caneta | Eventos de Toque -- quando ainda usar APIs específicas de toque | Eventos de Mouse -- contraparte legada
Carregue mais conteúdo quando o usuário atingir o final de um contêiner rolável.
"use client";
import { useState } from "react";
export default function FeedList({ initial }: { initial: string[] }) {
const [items, setItems] = useState(initial);
const [loading, setLoading] = useState(false);
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
const el = e.currentTarget;
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 100;
if (atBottom && !loading) {
setLoading(true);
fetch(`/api/feed?after=${items.length}`)
.then((r) => r.json())
.then((next: string[]) => setItems((prev) => [...prev, ...next]))
.finally(() => setLoading(false));
}
};
return (
<div onScroll={handleScroll} style={{ height: 400, overflow: "auto" }}>
{items.map((item) => (
<p key={item}>{item}</p>
))}
{loading && <p>Carregando mais...</p>}
</div>
);
}onScroll dispara com muita frequência -- proteja contra carregamento duplo com um sinalizador loading ou um debounce.scrollHeight - scrollTop - clientHeight em vez de igualdade exata para acionar ligeiramente antes.IntersectionObserver em um elemento sentinela -- dispara apenas quando o sentinela realmente entra na visualização.onScroll a window via React -- use useEffect + window.addEventListener("scroll", ...) para rolagem em nível de página.Relacionado: Eventos de Rolagem -- throttling, cabeçalhos fixos, restauração de rolagem | useIntersectionObserver -- padrão preferido para "carregar mais"
Implemente o arrastar e soltar do HTML5 entre duas listas usando os eventos de arrastar sintéticos do React.
"use client";
import { useState } from "react";
export default function Board() {
const [todo, setTodo] = useState(["Escrever docs", "Entregar funcionalidade"]);
const [done, setDone] = useState<string[]>([]);
const handleDragStart = (e: React.DragEvent<HTMLLIElement>, item: string) => {
e.dataTransfer.setData("text/plain", item);
};
const handleDrop = (e: React.DragEvent<HTMLUListElement>) => {
e.preventDefault();
const item = e.dataTransfer.getData("text/plain");
setTodo((prev) => prev.filter((i) => i !== item));
setDone((prev) => [...prev, item]);
};
return (
<div style={{ display: "flex", gap: "2rem" }}>
<ul>
{todo.map((item) => (
<li key={item} draggable onDragStart={(e) => handleDragStart(e, item)}>
{item}
</li>
))}
</ul>
<ul onDragOver={(e) => e.preventDefault()} onDrop={handleDrop}>
<strong>Feito</strong>
{done.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}e.preventDefault() em onDragOver ou o destino da soltura rejeitará a soltura.e.dataTransfer.setData(type, value) e leia-o de volta com getData -- esta é a única ponte segura entre janelas.dnd-kit ou similar.draggable ao elemento de origem; o React não o define para você.Relacionado: Eventos de Arrastar e Soltar -- onDragStart, onDragOver, onDrop | dnd-kit -- arrastar e soltar ergonômico com suporte a toque
Acompanhe quando um vídeo está pronto para reproduzir e quando ele termina.
"use client";
import { useRef, useState } from "react";
export default function VideoPlayer({ src }: { src: string }) {
const ref = useRef<HTMLVideoElement>(null);
const [status, setStatus] = useState<"loading" | "ready" | "ended">("loading");
return (
<div>
<video
ref={ref}
src={src}
controls
onLoadedMetadata={() => setStatus("ready")}
onEnded={() => setStatus("ended")}
/>
<p>Status: {status}</p>
{status === "ended" && (
<button onClick={() => ref.current?.play()}>Reproduzir novamente</button>
)}
</div>
);
}onLoadedMetadata dispara assim que o navegador conhece a duração e as dimensões do vídeo -- seguro para ler ref.current.duration.onEnded dispara quando a reprodução atinge o final; onPlay, onPause, onTimeUpdate cobrem o restante do ciclo de vida.<video> ou <audio>.onTransitionEnd e onAnimationEnd fornecem o mesmo sinal de término.Relacionado: Eventos de Mídia e Animação -- referência completa de mídia + eventos de animação | useRef -- acessando o nó DOM subjacente
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥