Requisições HTTP no React com fetch e axios
1. Introdução às requisições HTTP no ecossistema React
No desenvolvimento front-end moderno, as APIs são a espinha dorsal da comunicação entre o cliente e o servidor. No React, consumir dados de APIs externas é uma tarefa cotidiana — seja para carregar uma lista de usuários, enviar um formulário ou autenticar um usuário.
Duas abordagens dominam esse cenário: a Fetch API nativa do JavaScript e a biblioteca Axios. Ambas permitem realizar requisições HTTP, mas diferem em sintaxe, recursos e nível de abstração. Compreender ambas é essencial para qualquer desenvolvedor React, pois cada uma tem seu lugar — fetch é leve e nativa, enquanto axios oferece uma experiência mais robusta e produtiva.
Toda requisição HTTP segue um ciclo de vida básico: loading (enquanto a requisição está sendo processada), sucesso (dados retornados) ou erro (falha na comunicação ou resposta inesperada). Gerenciar esses três estados é fundamental para criar interfaces responsivas e amigáveis.
2. Usando a Fetch API nativa no React
A Fetch API é nativa do JavaScript moderno e não requer instalação. Sua sintaxe básica envolve chamar fetch() com a URL e, em seguida, processar a resposta com .json().
// GET simples com fetch
fetch('https://api.exemplo.com/usuarios')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Erro:', error));
Para operações CRUD completas:
// POST com fetch
const novoUsuario = { nome: 'João', email: 'joao@email.com' };
fetch('https://api.exemplo.com/usuarios', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(novoUsuario)
})
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(data => console.log('Criado:', data))
.catch(error => console.error('Erro:', error));
// PUT e DELETE seguem o mesmo padrão, alterando method e body
O tratamento de erros no fetch exige cuidado: a promessa do fetch() só rejeita em casos de falha de rede. Para erros HTTP (404, 500), precisamos verificar response.ok manualmente:
const fetchComTratamento = async () => {
try {
const response = await fetch('https://api.exemplo.com/recurso');
if (!response.ok) {
throw new Error(`Erro HTTP: ${response.status} - ${response.statusText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Falha na requisição:', error);
throw error;
}
};
3. Trabalhando com Axios no React
Axios é uma biblioteca popular que abstrai o trabalho com requisições HTTP. Primeiro, instale-a:
npm install axios
Operações CRUD com axios são mais concisas:
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.exemplo.com',
timeout: 5000
});
// GET
const usuarios = await api.get('/usuarios');
// POST
const novoUsuario = await api.post('/usuarios', { nome: 'Maria', email: 'maria@email.com' });
// PUT
const atualizado = await api.put('/usuarios/1', { nome: 'Maria Silva' });
// DELETE
await api.delete('/usuarios/1');
Uma das grandes vantagens do axios são os interceptors, que permitem interceptar requisições e respostas para adicionar headers, tokens ou logs automaticamente:
// Interceptor de requisição
api.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log('Requisição enviada:', config);
return config;
}, error => Promise.reject(error));
// Interceptor de resposta
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
console.log('Token expirado, redirecionando para login...');
// Lógica de refresh token ou redirecionamento
}
return Promise.reject(error);
}
);
4. Gerenciamento de estado das requisições com hooks
No React, usamos useState e useEffect para gerenciar o ciclo de vida das requisições:
import { useState, useEffect } from 'react';
import axios from 'axios';
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsuarios = async () => {
try {
setLoading(true);
const response = await axios.get('https://api.exemplo.com/usuarios');
setUsuarios(response.data);
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUsuarios();
}, []);
if (loading) return <div>Carregando...</div>;
if (error) return <div>Erro: {error}</div>;
return (
<ul>
{usuarios.map(usuario => (
<li key={usuario.id}>{usuario.nome}</li>
))}
</ul>
);
}
Cancelamento de requisições é uma boa prática para evitar memory leaks em componentes desmontados:
// Com fetch usando AbortController
useEffect(() => {
const controller = new AbortController();
fetch('https://api.exemplo.com/dados', { signal: controller.signal })
.then(response => response.json())
.then(data => setDados(data))
.catch(err => {
if (err.name !== 'AbortError') setError(err.message);
});
return () => controller.abort();
}, []);
// Com axios usando CancelToken
useEffect(() => {
const source = axios.CancelToken.source();
axios.get('https://api.exemplo.com/dados', { cancelToken: source.token })
.then(response => setDados(response.data))
.catch(err => {
if (!axios.isCancel(err)) setError(err.message);
});
return () => source.cancel('Componente desmontado');
}, []);
5. Padrões avançados de requisição no React
Custom hooks tornam o código mais reutilizável:
// Hook personalizado useFetch
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) throw new Error('Erro na requisição');
const json = await response.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
// Uso
function Componente() {
const { data, loading, error } = useFetch('https://api.exemplo.com/dados');
// ...
}
Requisições paralelas com Promise.all:
const [usuarios, posts] = await Promise.all([
axios.get('/usuarios'),
axios.get('/posts')
]);
Debouncing para buscas em tempo real:
function BuscaUsuario() {
const [termo, setTermo] = useState('');
const [resultados, setResultados] = useState([]);
const timerRef = useRef(null);
const handleChange = (e) => {
const valor = e.target.value;
setTermo(valor);
clearTimeout(timerRef.current);
timerRef.current = setTimeout(async () => {
if (valor.length > 2) {
const response = await axios.get(`/usuarios?busca=${valor}`);
setResultados(response.data);
}
}, 500);
};
return (
<div>
<input value={termo} onChange={handleChange} placeholder="Buscar usuário..." />
<ul>
{resultados.map(r => <li key={r.id}>{r.nome}</li>)}
</ul>
</div>
);
}
6. Tratamento de erros e feedback visual
Erros específicos merecem tratamento diferenciado:
const handleError = (error) => {
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 400:
return 'Dados inválidos. Verifique o formulário.';
case 401:
return 'Não autorizado. Faça login novamente.';
case 404:
return 'Recurso não encontrado.';
case 500:
return 'Erro interno do servidor. Tente novamente mais tarde.';
default:
return data?.message || 'Ocorreu um erro inesperado.';
}
} else if (error.request) {
return 'Sem resposta do servidor. Verifique sua conexão.';
} else {
return 'Erro ao configurar a requisição.';
}
};
Integração com react-toastify para notificações:
import { toast } from 'react-toastify';
const fetchDados = async () => {
try {
const response = await axios.get('/dados');
toast.success('Dados carregados com sucesso!');
return response.data;
} catch (error) {
toast.error(handleError(error));
throw error;
}
};
7. Comparação: fetch vs axios no React
| Característica | Fetch | Axios |
|---|---|---|
| Sintaxe | Mais verbosa, requer .json() manual |
Mais concisa, retorna JSON automaticamente |
| Tratamento de erros | Precisa verificar response.ok |
Rejeita automaticamente em erros HTTP |
| Interceptors | Não nativo | Suporte completo |
| Timeout | Implementação manual | timeout na configuração |
| Cancelamento | AbortController |
CancelToken |
| Tamanho | Nativo (0 KB) | ~14 KB (gzip) |
| Transformação | Manual | Automática de JSON |
Quando usar cada um:
- Fetch: Projetos pequenos, quando você quer evitar dependências externas, ou quando precisa de controle total sobre a requisição.
- Axios: Projetos médios a grandes, quando produtividade é prioridade, ou quando recursos como interceptors e timeout são necessários.
8. Boas práticas e conclusão
Organize suas chamadas de API em serviços separados:
// services/usuarioService.js
import axios from 'axios';
const API_URL = process.env.REACT_APP_API_URL;
const usuarioService = {
listar: () => axios.get(`${API_URL}/usuarios`),
criar: (dados) => axios.post(`${API_URL}/usuarios`, dados),
atualizar: (id, dados) => axios.put(`${API_URL}/usuarios/${id}`, dados),
deletar: (id) => axios.delete(`${API_URL}/usuarios/${id}`),
};
export default usuarioService;
Use variáveis de ambiente para URLs de API (arquivo .env):
REACT_APP_API_URL=https://api.exemplo.com/v1
Segurança: nunca exponha tokens ou chaves secretas no código front-end. Use variáveis de ambiente e, idealmente, um backend proxy para operações sensíveis.
Dominar requisições HTTP no React com fetch e axios é essencial para construir aplicações modernas. Enquanto fetch oferece simplicidade e zero dependências, axios proporciona uma experiência mais produtiva com recursos avançados. A escolha depende do seu projeto — mas conhecer ambos amplia seu arsenal como desenvolvedor.
Referências
- MDN Web Docs: Fetch API — Documentação oficial da Fetch API, com exemplos detalhados de uso.
- Axios GitHub Repository — Repositório oficial da biblioteca axios com documentação completa e exemplos.
- React Documentation: Effects — Documentação oficial do React sobre o hook useEffect, essencial para requisições em componentes.
- Using Axios with React: A Complete Guide — Tutorial prático da DigitalOcean sobre integração de axios com React.
- React Hooks: Custom Hook for Fetching Data — Artigo técnico de Robin Wieruch sobre criação de custom hooks para requisições HTTP no React.
- How to Handle HTTP Errors in React — Guia do freeCodeCamp sobre tratamento de erros HTTP em aplicações React.