1
2
Exemplos Práticos de Eventos
Veja como implementar diferentes tipos de eventos
Exemplos Práticos de Eventos
Eventos de Mouse
Click e Double Click
function BotoesInterativos() {
const [cliques, setCliques] = useState(0);
const [duplos, setDuplos] = useState(0);
const handleClick = () => {
setCliques(cliques + 1);
};
const handleDoubleClick = () => {
setDuplos(duplos + 1);
};
return (
<div>
<button onClick={handleClick}>Clique: {cliques}</button>
<button onDoubleClick={handleDoubleClick}>Duplo: {duplos}</button>
</div>
);
}
Hover (Mouse Enter/Leave)
function CardHover() {
const [hovering, setHovering] = useState(false);
return (
<div
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
style={{
padding: '20px',
backgroundColor: hovering ? '#e0e0e0' : '#f5f5f5',
transition: 'background-color 0.3s'
}}
>
{hovering ? 'Mouse sobre o card!' : 'Passe o mouse aqui'}
</div>
);
}
Eventos de Teclado
Detectando Teclas Específicas
function DetectorTeclas() {
const [ultimaTecla, setUltimaTecla] = useState('');
const [enter, setEnter] = useState(0);
const handleKeyDown = (e) => {
setUltimaTecla(e.key);
if (e.key === 'Enter') {
setEnter(enter + 1);
}
// Prevenir comportamento padrão para algumas teclas
if (e.key === 'Tab') {
e.preventDefault();
console.log('Tab foi prevenido');
}
};
return (
<div>
<input
type="text"
onKeyDown={handleKeyDown}
placeholder="Digite algo..."
/>
<p>Última tecla: {ultimaTecla}</p>
<p>Enter pressionado: {enter} vezes</p>
</div>
);
}
Atalhos de Teclado
function AtalhosTeclado() {
const [acao, setAcao] = useState('');
useEffect(() => {
const handleKeyPress = (e) => {
// Ctrl/Cmd + S
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
setAcao('Salvando...');
setTimeout(() => setAcao(''), 2000);
}
// Ctrl/Cmd + K
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
setAcao('Abrindo busca...');
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, []);
return (
<div>
<p>Tente Ctrl+S ou Ctrl+K</p>
{acao && <p style={{ color: 'green' }}>{acao}</p>}
</div>
);
}
Passando Parâmetros para Handlers
Usando Arrow Functions
function ListaItens() {
const itens = ['Maçã', 'Banana', 'Laranja'];
const [selecionado, setSelecionado] = useState('');
const handleClick = (item) => {
setSelecionado(item);
};
return (
<div>
<ul>
{itens.map(item => (
<li key={item}>
<button onClick={() => handleClick(item)}>
{item}
</button>
</li>
))}
</ul>
<p>Selecionado: {selecionado}</p>
</div>
);
}
Usando o Evento
function FormularioDinamico() {
const [valores, setValores] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setValores(prev => ({
...prev,
[name]: value
}));
};
return (
<form>
<input
name="nome"
placeholder="Nome"
onChange={handleChange}
/>
<input
name="email"
placeholder="Email"
onChange={handleChange}
/>
<pre>{JSON.stringify(valores, null, 2)}</pre>
</form>
);
}
Propagação de Eventos
Stopando Propagação
function PropagacaoEventos() {
const handleDivClick = () => {
console.log('Div clicada');
};
const handleButtonClick = (e) => {
e.stopPropagation(); // Impede que o evento suba para a div
console.log('Botão clicado');
};
return (
<div
onClick={handleDivClick}
style={{ padding: '50px', backgroundColor: '#f0f0f0' }}
>
<button onClick={handleButtonClick}>
Clique (sem propagar)
</button>
</div>
);
}
Eventos Customizados
Criando e Disparando
function ComponenteComEventoCustom() {
useEffect(() => {
const handleCustomEvent = (e) => {
console.log('Evento customizado recebido:', e.detail);
};
window.addEventListener('meuEventoCustom', handleCustomEvent);
return () => {
window.removeEventListener('meuEventoCustom', handleCustomEvent);
};
}, []);
const dispararEvento = () => {
const evento = new CustomEvent('meuEventoCustom', {
detail: { mensagem: 'Olá do React!' }
});
window.dispatchEvent(evento);
};
return (
<button onClick={dispararEvento}>
Disparar Evento Customizado
</button>
);
}
Performance com Eventos
Debouncing
function CampoBusca() {
const [busca, setBusca] = useState('');
const [resultados, setResultados] = useState([]);
useEffect(() => {
const timeoutId = setTimeout(() => {
if (busca) {
console.log('Buscando:', busca);
// Simular busca
setResultados([`Resultado para "${busca}"`]);
}
}, 500);
return () => clearTimeout(timeoutId);
}, [busca]);
return (
<div>
<input
type="text"
value={busca}
onChange={(e) => setBusca(e.target.value)}
placeholder="Digite para buscar..."
/>
<ul>
{resultados.map((r, i) => <li key={i}>{r}</li>)}
</ul>
</div>
);
}
Throttling
function ScrollInfinito() {
const [scrolls, setScrolls] = useState(0);
const [canScroll, setCanScroll] = useState(true);
const handleScroll = () => {
if (!canScroll) return;
setCanScroll(false);
setScrolls(prev => prev + 1);
setTimeout(() => {
setCanScroll(true);
}, 200); // Throttle de 200ms
};
return (
<div
onScroll={handleScroll}
style={{ height: '200px', overflow: 'auto' }}
>
<div style={{ height: '500px' }}>
<p>Scroll detectado: {scrolls} vezes</p>
<p>Role para baixo...</p>
</div>
</div>
);
}
Boas Práticas
- Use funções nomeadas para handlers complexos
- Evite criar funções inline em renders (performance)
- Sempre limpe listeners em useEffect
- Use preventDefault() quando necessário
- Considere debounce/throttle para eventos frequentes
3
Exercício: Jogo de Cliques
Crie um jogo interativo usando diferentes eventos
Activity
Exercício: Jogo de Cliques
Objetivo
Criar um jogo onde o usuário deve clicar em alvos que aparecem aleatoriamente na tela.
Requisitos
-
Alvo Móvel
- Quadrado ou círculo que aparece em posições aleatórias
- Desaparece após 2 segundos
- Reaparece em nova posição
-
Sistema de Pontuação
- +10 pontos por acerto
- -5 pontos por erro (clicar fora)
- Mostrar pontuação atual
-
Níveis de Dificuldade
- Fácil: alvo grande, 3 segundos
- Médio: alvo médio, 2 segundos
- Difícil: alvo pequeno, 1 segundo
-
Recursos Extras
- Contador de tempo
- Recorde de pontuação
- Botão para pausar/continuar
Template Inicial
import React, { useState, useEffect } from 'react';
function JogoDeCliques() {
const [posicao, setPosicao] = useState({ x: 0, y: 0 });
const [pontos, setPontos] = useState(0);
const [ativo, setAtivo] = useState(false);
const [dificuldade, setDificuldade] = useState('medio');
const configuracoes = {
facil: { tamanho: 80, tempo: 3000 },
medio: { tamanho: 60, tempo: 2000 },
dificil: { tamanho: 40, tempo: 1000 }
};
// Seu código aqui
return (
<div>
{/* Implemente a interface aqui */}
</div>
);
}
export default JogoDeCliques;
Dicas
- Use
Math.random()para posições aleatórias getBoundingClientRect()para limites da área de jogosetTimeoutpara controlar aparição/desaparecimento- Considere usar
position: absolutepara o alvo
Solução
Clique para ver a solução
import React, { useState, useEffect, useRef } from 'react';
function JogoDeCliques() {
const [posicao, setPosicao] = useState({ x: 0, y: 0 });
const [pontos, setPontos] = useState(0);
const [ativo, setAtivo] = useState(false);
const [visivel, setVisivel] = useState(false);
const [dificuldade, setDificuldade] = useState('medio');
const [tempo, setTempo] = useState(0);
const [recorde, setRecorde] = useState(
parseInt(localStorage.getItem('recorde') || '0')
);
const areaJogoRef = useRef(null);
const timeoutRef = useRef(null);
const intervalRef = useRef(null);
const configuracoes = {
facil: { tamanho: 80, tempo: 3000, cor: '#4caf50' },
medio: { tamanho: 60, tempo: 2000, cor: '#ff9800' },
dificil: { tamanho: 40, tempo: 1000, cor: '#f44336' }
};
const config = configuracoes[dificuldade];
const gerarPosicaoAleatoria = () => {
if (!areaJogoRef.current) return { x: 0, y: 0 };
const area = areaJogoRef.current.getBoundingClientRect();
const x = Math.random() * (area.width - config.tamanho);
const y = Math.random() * (area.height - config.tamanho);
return { x, y };
};
const mostrarAlvo = () => {
setPosicao(gerarPosicaoAleatoria());
setVisivel(true);
timeoutRef.current = setTimeout(() => {
setVisivel(false);
if (ativo) {
setTimeout(mostrarAlvo, 500);
}
}, config.tempo);
};
const handleClickAlvo = (e) => {
e.stopPropagation();
setPontos(prev => prev + 10);
setVisivel(false);
clearTimeout(timeoutRef.current);
if (ativo) {
setTimeout(mostrarAlvo, 300);
}
};
const handleClickFora = () => {
if (ativo && visivel) {
setPontos(prev => Math.max(0, prev - 5));
}
};
const iniciarJogo = () => {
setAtivo(true);
setPontos(0);
setTempo(0);
mostrarAlvo();
};
const pararJogo = () => {
setAtivo(false);
setVisivel(false);
clearTimeout(timeoutRef.current);
clearInterval(intervalRef.current);
if (pontos > recorde) {
setRecorde(pontos);
localStorage.setItem('recorde', pontos.toString());
}
};
useEffect(() => {
if (ativo) {
intervalRef.current = setInterval(() => {
setTempo(prev => prev + 1);
}, 1000);
}
return () => {
clearInterval(intervalRef.current);
};
}, [ativo]);
useEffect(() => {
return () => {
clearTimeout(timeoutRef.current);
clearInterval(intervalRef.current);
};
}, []);
return (
<div style={{
maxWidth: '800px',
margin: '0 auto',
padding: '20px',
textAlign: 'center'
}}>
<h1>Jogo de Cliques</h1>
<div style={{
display: 'flex',
justifyContent: 'space-around',
marginBottom: '20px'
}}>
<div>Pontos: {pontos}</div>
<div>Tempo: {tempo}s</div>
<div>Recorde: {recorde}</div>
</div>
{!ativo && (
<div style={{ marginBottom: '20px' }}>
<label>Dificuldade: </label>
<select
value={dificuldade}
onChange={(e) => setDificuldade(e.target.value)}
>
<option value="facil">Fácil</option>
<option value="medio">Médio</option>
<option value="dificil">Difícil</option>
</select>
</div>
)}
<button
onClick={ativo ? pararJogo : iniciarJogo}
style={{
marginBottom: '20px',
padding: '10px 20px',
fontSize: '16px'
}}
>
{ativo ? 'Parar Jogo' : 'Iniciar Jogo'}
</button>
<div
ref={areaJogoRef}
onClick={handleClickFora}
style={{
position: 'relative',
width: '100%',
height: '400px',
border: '2px solid #333',
backgroundColor: '#f5f5f5',
cursor: ativo ? 'crosshair' : 'default'
}}
>
{visivel && (
<div
onClick={handleClickAlvo}
style={{
position: 'absolute',
left: `${posicao.x}px`,
top: `${posicao.y}px`,
width: `${config.tamanho}px`,
height: `${config.tamanho}px`,
backgroundColor: config.cor,
borderRadius: '50%',
cursor: 'pointer',
transition: 'all 0.1s'
}}
/>
)}
</div>
{!ativo && pontos > 0 && (
<div style={{ marginTop: '20px' }}>
<h3>Fim de Jogo!</h3>
<p>Pontuação final: {pontos}</p>
{pontos === recorde && <p style={{ color: 'green' }}>Novo recorde!</p>}
</div>
)}
</div>
);
}
export default JogoDeCliques;
Desafios Extras
- Power-ups: Alvos especiais que dão mais pontos
- Combos: Bônus por acertos consecutivos
- Sons: Adicionar efeitos sonoros
- Ranking: Sistema de ranking online
- Modos de Jogo: Tempo limitado, vidas limitadas, etc.
3 content items