1
2
Padrões Comuns do useEffect
Exemplos práticos dos padrões mais usados com useEffect
Padrões Comuns do useEffect
1. Buscar Dados de uma API
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [carregando, setCarregando] = useState(true);
const [erro, setErro] = useState(null);
useEffect(() => {
const buscarUsuarios = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) throw new Error('Erro ao buscar dados');
const dados = await response.json();
setUsuarios(dados);
} catch (err) {
setErro(err.message);
} finally {
setCarregando(false);
}
};
buscarUsuarios();
}, []); // Array vazio = executa uma vez
if (carregando) return <p>Carregando...</p>;
if (erro) return <p>Erro: {erro}</p>;
return (
<ul>
{usuarios.map(usuario => (
<li key={usuario.id}>{usuario.name}</li>
))}
</ul>
);
}
2. Timer com Cleanup
function Relogio() {
const [tempo, setTempo] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
setTempo(new Date());
}, 1000);
// Cleanup: limpa o timer quando o componente desmonta
return () => {
clearInterval(timer);
};
}, []);
return <h2>{tempo.toLocaleTimeString()}</h2>;
}
3. Listener de Eventos
function TamanhoJanela() {
const [largura, setLargura] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setLargura(window.innerWidth);
};
window.addEventListener('resize', handleResize);
// Cleanup: remove o listener
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <p>Largura da janela: {largura}px</p>;
}
4. LocalStorage com Sincronização
function ConfiguracaoTema() {
const [tema, setTema] = useState(() => {
// Inicializa com valor do localStorage
return localStorage.getItem('tema') || 'claro';
});
useEffect(() => {
// Salva no localStorage quando tema muda
localStorage.setItem('tema', tema);
// Aplica tema ao documento
document.body.className = tema;
}, [tema]);
return (
<button onClick={() => setTema(tema === 'claro' ? 'escuro' : 'claro')}>
Mudar para tema {tema === 'claro' ? 'escuro' : 'claro'}
</button>
);
}
5. Debounce de Pesquisa
function PesquisaProdutos() {
const [termo, setTermo] = useState('');
const [resultados, setResultados] = useState([]);
useEffect(() => {
// Não pesquisa se o termo estiver vazio
if (!termo) {
setResultados([]);
return;
}
// Debounce: aguarda 500ms após parar de digitar
const timeoutId = setTimeout(() => {
// Simula busca de API
console.log('Pesquisando por:', termo);
// fetch(`/api/produtos?q=${termo}`).then(...);
}, 500);
// Cleanup: cancela o timeout se o usuário continuar digitando
return () => clearTimeout(timeoutId);
}, [termo]);
return (
<div>
<input
type="text"
value={termo}
onChange={(e) => setTermo(e.target.value)}
placeholder="Pesquisar produtos..."
/>
</div>
);
}
6. Dependências Múltiplas
function CalculadoraPreco({ quantidade, precoUnitario }) {
const [total, setTotal] = useState(0);
const [desconto, setDesconto] = useState(0);
useEffect(() => {
// Recalcula quando quantidade OU preço mudam
const subtotal = quantidade * precoUnitario;
// Aplica desconto progressivo
if (quantidade >= 10) {
setDesconto(0.1); // 10% de desconto
} else if (quantidade >= 5) {
setDesconto(0.05); // 5% de desconto
} else {
setDesconto(0);
}
setTotal(subtotal * (1 - desconto));
}, [quantidade, precoUnitario, desconto]);
return (
<div>
<p>Subtotal: R$ {(quantidade * precoUnitario).toFixed(2)}</p>
{desconto > 0 && <p>Desconto: {(desconto * 100)}%</p>}
<p>Total: R$ {total.toFixed(2)}</p>
</div>
);
}
Dicas Importantes
1. Evite Loops Infinitos
// ❌ ERRADO: Causa loop infinito
useEffect(() => {
setCount(count + 1);
}); // Sem dependências = executa sempre
// ✅ CORRETO: Executa uma vez
useEffect(() => {
setCount(count + 1);
}, []);
2. Não Esqueça do Cleanup
// ❌ Vazamento de memória
useEffect(() => {
const timer = setInterval(() => {}, 1000);
}, []);
// ✅ Com cleanup
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
}, []);
3. Use Dependências Corretas
// ⚠️ Aviso do ESLint sobre dependências faltando
useEffect(() => {
console.log(count); // usa 'count' mas não lista como dependência
}, []); // ESLint avisará sobre isso
// ✅ Correto
useEffect(() => {
console.log(count);
}, [count]);
3
Exercício: Relógio com Fuso Horário
Crie um relógio que atualiza em tempo real com seleção de fuso horário
Activity
Exercício: Relógio com Fuso Horário
Objetivo
Criar um componente de relógio que:
- Mostra a hora atual
- Atualiza a cada segundo
- Permite selecionar diferentes fusos horários
- Limpa corretamente o timer quando necessário
Requisitos
- Use
useStatepara armazenar:- Hora atual
- Fuso horário selecionado
- Use
useEffectpara:- Criar um timer que atualiza a cada segundo
- Limpar o timer quando o componente desmontar
- Reagir a mudanças no fuso horário
- Implemente um select com pelo menos 4 fusos horários
- Formate a hora de forma legível
Template Inicial
import React, { useState, useEffect } from 'react';
function RelogioFusoHorario() {
// Fusos horários disponíveis
const fusos = [
{ nome: 'Brasília', offset: -3 },
{ nome: 'Nova York', offset: -5 },
{ nome: 'Londres', offset: 0 },
{ nome: 'Tóquio', offset: 9 }
];
// Seu código aqui
return (
<div>
{/* Implemente a interface aqui */}
</div>
);
}
export default RelogioFusoHorario;
Dicas
- Use
new Date()para obter a hora atual - Para ajustar o fuso horário, use:
const horaUTC = data.getTime() + (data.getTimezoneOffset() * 60000); const horaFuso = new Date(horaUTC + (3600000 * offset)); - Use
toLocaleTimeString()para formatar a hora - Não esqueça de retornar a função de cleanup no useEffect
Solução
Clique para ver a solução
import React, { useState, useEffect } from 'react';
function RelogioFusoHorario() {
const fusos = [
{ nome: 'Brasília', offset: -3 },
{ nome: 'Nova York', offset: -5 },
{ nome: 'Londres', offset: 0 },
{ nome: 'Tóquio', offset: 9 },
{ nome: 'Sydney', offset: 11 },
{ nome: 'Dubai', offset: 4 }
];
const [hora, setHora] = useState(new Date());
const [fusoSelecionado, setFusoSelecionado] = useState(fusos[0]);
useEffect(() => {
const timer = setInterval(() => {
setHora(new Date());
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
const calcularHoraFuso = () => {
const horaUTC = hora.getTime() + (hora.getTimezoneOffset() * 60000);
const horaFuso = new Date(horaUTC + (3600000 * fusoSelecionado.offset));
return horaFuso;
};
const horaFormatada = calcularHoraFuso().toLocaleTimeString('pt-BR');
const dataFormatada = calcularHoraFuso().toLocaleDateString('pt-BR');
return (
<div style={{ textAlign: 'center', padding: '20px' }}>
<h1>Relógio Mundial</h1>
<div style={{ margin: '20px' }}>
<label htmlFor="fuso">Selecione o fuso horário: </label>
<select
id="fuso"
value={fusoSelecionado.nome}
onChange={(e) => {
const fuso = fusos.find(f => f.nome === e.target.value);
setFusoSelecionado(fuso);
}}
>
{fusos.map(fuso => (
<option key={fuso.nome} value={fuso.nome}>
{fuso.nome} (UTC{fuso.offset >= 0 ? '+' : ''}{fuso.offset})
</option>
))}
</select>
</div>
<div style={{
fontSize: '48px',
fontFamily: 'monospace',
margin: '20px',
padding: '20px',
border: '2px solid #333',
borderRadius: '10px',
backgroundColor: '#f0f0f0'
}}>
{horaFormatada}
</div>
<div style={{ fontSize: '20px', color: '#666' }}>
{dataFormatada}
</div>
<div style={{ marginTop: '20px', fontSize: '14px', color: '#888' }}>
<p>Hora local do seu navegador: {new Date().toLocaleTimeString('pt-BR')}</p>
</div>
</div>
);
}
export default RelogioFusoHorario;
Desafio Extra
Adicione as seguintes funcionalidades:
- Formato 12/24 horas: Toggle para alternar entre formatos
- Múltiplos relógios: Permitir adicionar vários relógios com fusos diferentes
- Alarme: Adicionar funcionalidade de alarme para um horário específico
- Tema: Alternar entre tema claro e escuro
- Persistência: Salvar as preferências no localStorage
3 content items