use-http — минималистичные запросы через хуки
Содержание
- Введение: зачем нужен use-http
- Установка и первый запрос
- Базовые HTTP-методы
- Опции и настройка
- Управление запросами вручную
- Отмена запросов (Abort)
- Повторные попытки (Retries)
- GraphQL в use-http
- use-http vs Axios vs React Query
- Частые ошибки новичков
- Итог (что мы узнали)
- Задание для закрепления (на 1–2 часа)
- Что дальше?
Цель урока: познакомиться с библиотекой use-http, которая позволяет выполнять HTTP-запросы через React-хуки с минимальным количеством кода. Научиться использовать её для быстрого прототипирования и небольших проектов, где не требуется сложное кеширование.
Введение: зачем нужен use-http
В предыдущих уроках мы рассмотрели fetch, axios и react-query. У каждого из них есть свои сильные стороны. use-http — это ещё один подход к работе с API в React.
Главная мысль: use-http — это React-хук для выполнения HTTP-запросов, который объединяет в себе простоту fetch и удобство работы с состояниями загрузки и ошибок прямо внутри компонента.
Ключевые особенности
- Минимальный код — один хук заменяет
useState+useEffectдля загрузки данных - Встроенные состояния —
loading,error,dataвозвращаются автоматически - Поддержка SSR — работает как на клиенте, так и на сервере
- GraphQL — поддерживает запросы и мутации
- Автоотмена запросов — при размонтировании компонента
Когда использовать use-http
- Небольшие React-проекты без сложного кеширования
- Быстрое прототипирование
- Когда нужен простой и понятный код без лишних зависимостей
- Вы предпочитаете подход «всё в одном хуке»
Установка и первый запрос
Установка:
npm install use-http
Простейший GET-запрос:
import useFetch from 'use-http';
function Todos() {
// Хук сам выполняет GET-запрос при монтировании
const { data, loading, error } = useFetch('https://api.example.com/todos', []);
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error.message}</p>;
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
Что здесь происходит
useFetch(url, options, dependencies)— принимает URL, опции и массив зависимостей- Третий аргумент
[]означает: выполнить запрос один раз при монтировании (аналогuseEffect(() => {}, [])) data— ответ сервера (уже распарсенный JSON)loading— флаг загрузки (trueво время запроса)error— объект ошибки, если запрос не удался
Базовые HTTP-методы
use-http предоставляет удобные методы для всех типов запросов:
GET (получение данных):
function UserProfile({ userId }) {
const { data, loading, error, get } = useFetch('/api/users');
const loadUser = () => {
get(`/${userId}`);
};
return (
<div>
<button onClick={loadUser}>Загрузить пользователя</button>
{loading && <p>Загрузка...</p>}
{error && <p>Ошибка: {error.message}</p>}
{data && <h1>{data.name}</h1>}
</div>
);
}
POST (создание данных):
function AddTodo() {
const { post, loading, error, response } = useFetch('/api/todos');
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const newTodo = await post({
title: formData.get('title'),
completed: false
});
if (response.ok) {
console.log('Todo создан!', newTodo);
}
};
return (
<form onSubmit={handleSubmit}>
<input name="title" placeholder="Новая задача" />
<button type="submit" disabled={loading}>
{loading ? 'Отправка...' : 'Добавить'}
</button>
{error && <p>Ошибка: {error.message}</p>}
</form>
);
}
Все методы:
const { get, post, put, patch, delete: del } = useFetch('/api/posts');
// DELETE (используем `del`, т.к. `delete` — зарезервированное слово)
await del('/1');
Опции и настройка
Настройка через объект опций:
const { data, loading, error } = useFetch('/api/data', {
// Данные по умолчанию (чтобы не было undefined)
data: [],
// Заголовки
headers: {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json'
},
// Выполнить запрос при монтировании
onMount: true,
// Данные для POST/PUT (если нужно)
body: { name: 'Алексей' },
// Параметры URL (аналог query-строки)
params: { page: 1, limit: 10 }
});
Provider для глобальных настроек:
import { Provider } from 'use-http';
function App() {
const options = {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
};
return (
<Provider url="https://api.example.com" options={options}>
<Todos />
<Users />
</Provider>
);
}
// Теперь во всех дочерних компонентах можно использовать относительные пути:
function Todos() {
const { data } = useFetch('/todos', []); // → https://api.example.com/todos
// ...
}
Управление запросами вручную
Выполнение запроса по событию:
function SearchGithub() {
const [query, setQuery] = useState('');
const { get, data, loading, error } = useFetch(
'https://api.github.com/search/repositories?q='
);
const search = (e) => {
const value = e.target.value;
setQuery(value);
if (value.length > 2) {
get(encodeURI(value)); // GET с параметром в URL
}
};
return (
<div>
<input onChange={search} placeholder="Поиск репозиториев..." />
{loading && <p>Поиск...</p>}
{error && <p>Ошибка: {error.message}</p>}
{data?.items?.map(repo => (
<div key={repo.id}>{repo.name}</div>
))}
</div>
);
}
Отмена запросов (Abort)
use-http автоматически отменяет запросы при размонтировании компонента. Но можно и вручную:
function CancelableRequest() {
const { get, abort, loading, data } = useFetch('/api/slow-data');
const loadData = () => get();
return (
<div>
<button onClick={loadData}>Загрузить</button>
<button onClick={abort}>Отменить</button>
{loading && <p>Загрузка...</p>}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}
Повторные попытки (Retries)
Библиотека поддерживает автоматические повторные попытки при ошибках:
const { data, error, get } = useFetch('/api/unreliable', {
retries: 3, // Максимум 3 попытки
retryDelay: 1000, // Задержка между попытками (1 секунда)
retryOn: [500, 503], // Коды ответов, при которых повторять
});
// Или с кастомной логикой:
const { data } = useFetch('/api/data', {
retries: 3,
retryOn: async ({ attempt, error, response }) => {
// Возвращаем true, если нужно повторить
return error || (response && response.status >= 500);
},
retryDelay: ({ attempt }) => {
// Экспоненциальная задержка
return Math.min(2 ** attempt * 1000, 30000);
}
});
GraphQL в use-http
use-http поддерживает GraphQL через отдельные хуки:
GraphQL Query:
import { useQuery } from 'use-http';
const QUERY = `
query Todos($userID: String!) {
todos(userID: $userID) {
id
title
completed
}
}
`;
function UserTodos({ userId }) {
const [data, loading, error, query] = useQuery(QUERY);
const loadTodos = () => query({ userID: userId });
return (
<div>
<button onClick={loadTodos}>Загрузить задачи</button>
{loading && <p>Загрузка...</p>}
{error && <p>Ошибка: {error.message}</p>}
{data?.todos?.map(todo => (
<div key={todo.id}>{todo.title}</div>
))}
</div>
);
}
GraphQL Mutation:
import { useQuery } from 'use-http';
import { useMutation } from 'use-http';
const MUTATION = `
mutation CreateTodo($title: String!) {
todo(title: $title) {
id
title
}
}
`;
function AddTodoGraphQL() {
const [data, loading, error, mutate] = useMutation(MUTATION);
const [title, setTitle] = useState('');
const createTodo = () => mutate({ title });
return (
<div>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
<button onClick={createTodo} disabled={loading}>
Добавить
</button>
{data && <p>Создано: {data.todo.title}</p>}
</div>
);
}
const QUERY = `
query Todos($userID: String!) {
todos(userID: $userID) {
id
title
completed
}
}
`;
function UserTodos({ userId }) {
const [data, loading, error, query] = useQuery(QUERY);
const loadTodos = () => query({ userID: userId });
return (
<div>
<button onClick={loadTodos}>Загрузить задачи</button>
{loading && <p>Загрузка...</p>}
{error && <p>Ошибка: {error.message}</p>}
{data?.todos?.map(todo => (
<div key={todo.id}>{todo.title}</div>
))}
</div>
);
}
use-http vs Axios vs React Query
Сравнение поможет выбрать правильный инструмент:
| Характеристика | use-http | Axios | React Query |
|---|---|---|---|
| Размер бандла | ~4kb | ~4kb | ~16kb |
| Кеширование | ❌ Нет | ❌ Нет | ✅ Отличное |
| Состояния (loading/error) | ✅ Встроены | ❌ Вручную | ✅ Встроены |
| Интерсепторы | ❌ Нет | ✅ Есть | ❌ Нет |
| Автоотмена запросов | ✅ Есть | Через AbortController |
✅ Есть |
| Повторные попытки | ✅ Есть | ❌ Нет | ✅ Есть |
| Фоновое обновление | ❌ Нет | ❌ Нет | ✅ Есть |
| GraphQL | ✅ Есть | ❌ Нет | ✅ Есть (через плагин) |
Рекомендации по выбору:
| Сценарий | Рекомендация |
|---|---|
| Быстрое прототипирование | use-http |
| Небольшое приложение с API | use-http |
| Приложение с простыми запросами | Axios или use-http |
| Приложение с авторизацией и интерсепторами | Axios |
| Сложное приложение с кешированием и синхронизацией | React Query |
Частые ошибки новичков
| Ошибка | Почему | Исправление |
|---|---|---|
Деструктурируют response объект |
response.ok может быть stale-значением |
Используй response целиком, не деструктурируй |
| Забывают массив зависимостей | Запрос выполняется при каждом рендере | Добавь [] для однократного запроса |
Используют delete вместо del |
delete — зарезервированное слово в JS |
Используй del |
Не обрабатывают error |
Пользователь не видит проблему | Всегда проверяй error и показывай сообщение |
Не используют Provider |
Дублируют baseURL в каждом хуке |
Создай Provider на верхнем уровне |
Итог (что мы узнали)
| Что узнали | Как это работает |
|---|---|
useFetch |
Главный хук для HTTP-запросов |
| Состояния | data, loading, error возвращаются автоматически |
| Все методы | get, post, put, patch, del |
Provider |
Глобальные настройки (baseURL, заголовки) |
| Отмена запросов | abort() или автоотмена при размонтировании |
| Повторные попытки | retries, retryDelay, retryOn |
| GraphQL | useQuery и useMutation |
Задание для закрепления (на 1–2 часа)
Создай небольшое приложение с использованием use-http:
- Настройка: Установи
use-httpи создайProviderсbaseURLк публичному API (JSONPlaceholder) - Список постов: Используй
useFetchдля загрузки постов при монтировании. Показывайloadingиerror. - Поиск: Добавь поле поиска, которое отправляет GET-запрос с параметром q при вводе (используй
get()метод с URL) - Создание поста: Используй
post()метод для отправки нового поста. Показывайloadingна кнопке. - Удаление: Добавь кнопку удаления с использованием
del()
Дополнительно:
- Добавь кнопку «Отмена» для запроса поиска
- Добавь автоматический повтор при ошибке (3 попытки)
Проверка: Убедись, что все операции работают, состояния загрузки отображаются, а ошибки не ломают приложение.
Пример выполнения задания
Что дальше?
В следующем уроке разберём формы в React (React Hook Form, Formik, Final Form) — как управлять формами, валидацией и отправкой данных. Ты узнаешь, как создавать сложные формы с минимальным кодом, обрабатывать ошибки, работать с кастомными компонентами и управлять состоянием формы эффективно. Это превратит твои формы из скучных в мощные и удобные для пользователей.