Pedir dados a uma API
Obter dados de um servidor é um efeito colateral clássico: acontece fora do
React, é assíncrono e não deve acontecer durante o render. Por isso o padrão habitual
é disparar a requisição dentro de um useEffect e guardar o resultado no estado.
Como a rede demora e pode falhar, isso é modelado com três estados:
loading→ a requisição está em andamento (ainda não há dados).error→ a requisição falhou (mostramos uma mensagem).data→ os dados chegaram (nós os renderizamos).
function Usuario({ id }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ativo = true; // para ignorar respostas obsoletas
setLoading(true);
setError(null);
fetch("/api/usuarios/" + id)
.then((res) => {
if (!res.ok) throw new Error("Resposta " + res.status);
return res.json();
})
.then((json) => {
if (ativo) { setData(json); setLoading(false); }
})
.catch((err) => {
if (ativo) { setError(err.message); setLoading(false); }
});
return () => { ativo = false; }; // limpeza: evita "race conditions"
}, [id]);
if (loading) return <p>Carregando…</p>;
if (error) return <p>Erro: {error}</p>;
return <h2>{data.nome}</h2>;
}
Pontos-chave deste padrão:
- Enquanto os dados chegam, mostra-se um estado de carregamento (
Carregando…). O usuário nunca vê uma tela quebrada à espera da rede. - A requisição vai no
useEffectcom[id]como dependência: se oidmudar, o usuário correto é carregado novamente. - A limpeza (
ativo = false) descarta respostas que chegam tarde, quando o componente já pediu outra coisa ou foi desmontado (evita as race conditions).
Em aplicações reais essa lógica costuma ser delegada a bibliotecas como React Query ou SWR, que gerenciam cache, retentativas e estados para você. Mas todas se apoiam neste mesmo padrão
loading / error / data.