Axios — профессиональные HTTP-запросы в React
Содержание
- Введение: fetch vs Axios
- Установка и первый запрос
- Все HTTP-методы в Axios
- Создание экземпляра Axios (baseURL)
- Интерсепторы — перехват запросов и ответов
- Обработка ошибок в Axios
- Отмена запросов (AbortController)
- Axios с React Query (идеальная пара)
- axios vs fetch (сравнение)
- Частые ошибки новичков
- Задание для закрепления (на 1–2 часа)
- Что дальше?
Цель урока: научиться использовать Axios для работы с HTTP-запросами вместо нативного fetch. Понять, как настраивать интерсепторы, обрабатывать ошибки, создавать экземпляры клиента и упрощать работу с API в React-приложениях.
Введение: fetch vs Axios
В предыдущих уроках мы использовали нативный fetch для запросов к серверу. Он работает, но имеет ограничения.
Проблемы fetch
- Нет автоматической трансформации JSON — нужно дважды вызывать
.json() - Не бросает ошибки при статусах
4xx/5xx— нужно проверятьresponse.okвручную - Нет интерсепторов — сложно добавлять заголовки или логировать запросы
- Нет отмены запросов — требует дополнительного кода
- Громоздкий синтаксис — много шаблонного кода
Главная мысль: Axios — это HTTP-клиент, который решает все проблемы fetch. Он даёт удобный API, автоматическую трансформацию JSON, интерсепторы, отмену запросов и единую обработку ошибок.
Сравнение fetch и Axios
| Характеристика | fetch | Axios |
|---|---|---|
| Браузерная поддержка | Встроен | Требуется установка |
| JSON по умолчанию | ❌ Нет (нужен .json()) |
✅ Автоматически |
Ошибки 4xx/5xx |
Не бросает ошибки | ✅ Бросает ошибки |
| Интерсепторы | ❌ Нет | ✅ Есть |
| Отмена запросов | Через AbortController |
✅ Встроенная |
| Перехват ответов | ❌ Нет | ✅ Есть |
| Размер | 0 байт (встроен) | ~4kb |
Установка и первый запрос
Установка:
npm install axios
Первый GET-запрос:
import axios from 'axios';
function Posts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get('https://api.example.com/posts')
.then(response => {
setPosts(response.data);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error}</p>;
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
Что происходит:
axios.get()— выполняет GET-запросresponse.data— уже готовый объект JSON (не нужно вызывать.json()!)catch— ловит любые ошибки (включая 404, 500 и сетевые)
Все HTTP-методы в Axios
import axios from 'axios';
// GET — получение данных
const getData = async () => {
const response = await axios.get('/api/users');
return response.data;
};
// POST — создание данных
const createPost = async (post) => {
const response = await axios.post('/api/posts', post);
return response.data;
};
// PUT — полное обновление
const updatePost = async (id, post) => {
const response = await axios.put(`/api/posts/${id}`, post);
return response.data;
};
// PATCH — частичное обновление
const patchPost = async (id, updates) => {
const response = await axios.patch(`/api/posts/${id}`, updates);
return response.data;
};
// DELETE — удаление
const deletePost = async (id) => {
await axios.delete(`/api/posts/${id}`);
};
GET с параметрами:
// Способ 1 — через params (рекомендуется)
const response = await axios.get('/api/users', {
params: {
page: 2,
limit: 10,
sort: 'asc'
}
});
// Способ 2 — через URL
const response = await axios.get('/api/users?page=2&limit=10');
POST с заголовками:
const response = await axios.post('/api/posts',
{ title: 'Новый пост' },
{
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
}
}
);
Создание экземпляра Axios (baseURL)
Вместо повторения базового URL в каждом запросе создают экземпляр:
// api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000, // таймаут 5 секунд
headers: {
'Content-Type': 'application/json'
}
});
export default api;
Использование:
import api from './api';
// Все запросы теперь используют baseURL
const response = await api.get('/posts'); // → https://api.example.com/posts
const response = await api.post('/users', { name: 'Алексей' }); // → https://api.example.com/users
Оживи свой сайт. Освой JavaScript!
Статичная верстка — это только скелет. Наш онлайн-курс "JavaScript с нуля до профи" даст твоим страницам мышцы и нервы. Научись создавать слайдеры, формы, интерактивные карты и получать данные с сервера.
От теории — к реальным скриптам в твоём портфолио.
Интерсепторы — перехват запросов и ответов
Интерсепторы запросов — выполняются перед отправкой запроса:
// Добавляем токен авторизации ко всем запросам
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
Интерсепторы ответов — выполняются после получения ответа:
// Обрабатываем ошибки глобально
api.interceptors.response.use(
(response) => {
// Любой ответ с 2xx
return response;
},
(error) => {
// Любой ответ с 4xx/5xx
if (error.response?.status === 401) {
// Не авторизован — редирект на страницу входа
window.location.href = '/login';
}
if (error.response?.status === 500) {
console.error('Ошибка сервера:', error.response.data);
}
return Promise.reject(error);
}
);
Полный пример:
// api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
// Request interceptor
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
// Сервер ответил с ошибкой
console.error('Ошибка сервера:', error.response.status);
if (error.response.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
} else if (error.request) {
// Запрос был отправлен, но нет ответа
console.error('Нет ответа от сервера');
} else {
// Ошибка при настройке запроса
console.error('Ошибка запроса:', error.message);
}
return Promise.reject(error);
}
);
export default api;
Обработка ошибок в Axios
import api from './api';
async function fetchUser(id) {
try {
const response = await api.get(`/users/${id}`);
return response.data;
} catch (error) {
if (error.response) {
// Сервер ответил с ошибкой
console.error('Статус:', error.response.status);
console.error('Данные:', error.response.data);
console.error('Заголовки:', error.response.headers);
// Разные коды ошибок
switch (error.response.status) {
case 400:
throw new Error('Неверный запрос');
case 401:
throw new Error('Не авторизован');
case 403:
throw new Error('Доступ запрещён');
case 404:
throw new Error('Пользователь не найден');
case 500:
throw new Error('Ошибка сервера');
default:
throw new Error('Неизвестная ошибка');
}
} else if (error.request) {
// Запрос был отправлен, но ответ не получен
console.error('Нет ответа от сервера');
throw new Error('Сервер не отвечает');
} else {
// Ошибка при настройке запроса
console.error('Ошибка:', error.message);
throw error;
}
}
}
Отмена запросов (AbortController)
Axios поддерживает отмену запросов через AbortController:
import { useState, useEffect } from 'react';
import api from './api';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const controller = new AbortController();
const fetchResults = async () => {
try {
const response = await api.get('/search', {
params: { q: query },
signal: controller.signal
});
setResults(response.data);
} catch (error) {
if (error.name === 'CanceledError') {
console.log('Запрос отменён');
} else {
console.error('Ошибка:', error);
}
}
};
if (query) {
fetchResults();
}
return () => controller.abort(); // Отменяем запрос при размонтировании
}, [query]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Поиск..."
/>
<ul>
{results.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
Преимущества:
- Отменяет запрос при быстром вводе
- Предотвращает race conditions
- Экономит трафик и ресурсы
Axios с React Query (идеальная пара)
Axios и React Query отлично работают вместе:
import { useQuery, useMutation } from '@tanstack/react-query';
import api from './api';
// Запрос через useQuery + Axios
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const response = await api.get('/users');
return response.data;
}
});
// Мутация через useMutation + Axios
const createUser = useMutation({
mutationFn: (newUser) => api.post('/users', newUser),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
}
});
return (
<div>
{isLoading && <p>Загрузка...</p>}
{error && <p>Ошибка: {error.message}</p>}
{data && <UserList users={data} />}
<button onClick={() => createUser.mutate({ name: 'Новый пользователь' })}>
Добавить
</button>
</div>
);
}
Преимущества:
- React Query управляет кешированием и состоянием
- Axios управляет HTTP-запросами
- Идеальное разделение ответственности
axios vs fetch (сравнение)
| Характеристика | fetch | Axios |
|---|---|---|
| Установка | Встроенный | Требуется npm install |
| Размер | 0 байт | ~4kb |
| JSON по умолчанию | ❌ Нет | ✅ Да |
Ошибки 4xx/5xx |
❌ Не бросает | ✅ Бросает |
| Интерсепторы | ❌ Нет | ✅ Есть |
| Отмена запросов | AbortController |
Встроенная |
| Прогресс загрузки | ❌ Нет | ✅ Есть |
| Старые браузеры | Требует полифилл | Работает |
| Параметры URL | Вручную | params |
Когда использовать Axios
- Сложное приложение с множеством запросов
- Нужны интерсепторы для токенов и ошибок
- Команда привыкла к Axios
Когда использовать fetch
- Простое приложение с 2–3 запросами
- Нет необходимости в интерсепторах
- Хочется минимизировать зависимости
Частые ошибки новичков
| Ошибка | Почему | Исправление |
|---|---|---|
Забывают .data из ответа |
Ошибка: response.data — объект, response — не объект |
Всегда используй response.data |
| Не обрабатывают ошибки ответа (4xx/5xx) | Axios бросает ошибки, но их нужно ловить | Всегда добавляй try/catch или .catch() |
| Не используют экземпляр | Дублируют baseURL в каждом запросе |
Создай экземпляр через axios.create() |
| Забывают отменять запросы | Memory leaks и race conditions | Используй AbortController |
| Не используют интерсепторы для токенов | Код с токеном дублируется | Добавь интерсептор для авторизации |
Задание для закрепления (на 1–2 часа)
Создай полноценное приложение «Менеджер задач» с использованием Axios:
- Настройка: Создай экземпляр Axios с
baseURLк публичному API (JSONPlaceholder) - GET запросы: Загрузи список задач через
useEffectи покажи их - POST запрос: Добавь новую задачу с отправкой на сервер
- PUT запрос: Обнови статус задачи (выполнена/не выполнена)
- DELETE запрос: Удали задачу
- Интерсепторы: добавь логгирование всех запросов в консоль и обработку ошибок (показывай тост при ошибке)
- Отмена запросов: Отменяй запрос при размонтировании компонента
Дополнительно:
- Добавь пагинацию через параметры
_pageи_limit - Добавь фильтрацию задач по статусу (все/выполненные/активные)
Проверка: убедись, что все CRUD-операции работают, ошибки обрабатываются, а интерсепторы логируют запросы.
Пример выполненного задания
Код выполненного задания скачать с GitHub: (откроется в новой вкладке)Axios в React
Что дальше?
В следующем уроке разберём use-http — лёгкую альтернативу Axios, построенную на хуках. Ты узнаешь, как работать с HTTP-запросами с минимальным кодом, используя декларативный подход на основе React-хуков.