Хотите заказать веб-сайт? Связаться с нами

use-http — минималистичные запросы через хуки

Цель урока: познакомиться с библиотекой use-http, которая позволяет выполнять HTTP-запросы через React-хуки с минимальным количеством кода. Научиться использовать её для быстрого прототипирования и небольших проектов, где не требуется сложное кеширование.

use-http — минималистичные запросы через хуки

Введение: зачем нужен use-http

В предыдущих уроках мы рассмотрели fetch, axios и react-query. У каждого из них есть свои сильные стороны. use-http — это ещё один подход к работе с API в React.

Главная мысль: use-http — это React-хук для выполнения HTTP-запросов, который объединяет в себе простоту fetch и удобство работы с состояниями загрузки и ошибок прямо внутри компонента.

Ключевые особенности

  1. Минимальный код — один хук заменяет useState + useEffect для загрузки данных
  2. Встроенные состоянияloading, error, data возвращаются автоматически
  3. Поддержка SSR — работает как на клиенте, так и на сервере
  4. GraphQL — поддерживает запросы и мутации
  5. Автоотмена запросов — при размонтировании компонента

Когда использовать use-http

  1. Небольшие React-проекты без сложного кеширования
  2. Быстрое прототипирование
  3. Когда нужен простой и понятный код без лишних зависимостей
  4. Вы предпочитаете подход «всё в одном хуке»

Установка и первый запрос

Установка:

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>
  );
}

Что здесь происходит

  1. useFetch(url, options, dependencies) — принимает URL, опции и массив зависимостей
  2. Третий аргумент [] означает: выполнить запрос один раз при монтировании (аналог useEffect(() => {}, []))
  3. data — ответ сервера (уже распарсенный JSON)
  4. loading — флаг загрузки (true во время запроса)
  5. 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
  // ...
}
Из ноутбука появляются летающие светящиеся экраны

Оживи свой сайт. Освой JavaScript!

Статичная верстка — это только скелет. Наш онлайн-курс "JavaScript с нуля до профи" даст твоим страницам мышцы и нервы. Научись создавать слайдеры, формы, интерактивные карты и получать данные с сервера.

От теории — к реальным скриптам в твоём портфолио.

Подробнее о курсе

Управление запросами вручную

Выполнение запроса по событию:

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:

  1. Настройка: Установи use-http и создай Provider с baseURL к публичному API (JSONPlaceholder)
  2. Список постов: Используй useFetch для загрузки постов при монтировании. Показывай loading и error.
  3. Поиск: Добавь поле поиска, которое отправляет GET-запрос с параметром q при вводе (используй get() метод с URL)
  4. Создание поста: Используй post() метод для отправки нового поста. Показывай loading на кнопке.
  5. Удаление: Добавь кнопку удаления с использованием del()

Дополнительно:

  1. Добавь кнопку «Отмена» для запроса поиска
  2. Добавь автоматический повтор при ошибке (3 попытки)

Проверка: Убедись, что все операции работают, состояния загрузки отображаются, а ошибки не ломают приложение.

Пример выполнения задания

Что дальше?

В следующем уроке разберём формы в React (React Hook Form, Formik, Final Form) — как управлять формами, валидацией и отправкой данных. Ты узнаешь, как создавать сложные формы с минимальным кодом, обрабатывать ошибки, работать с кастомными компонентами и управлять состоянием формы эффективно. Это превратит твои формы из скучных в мощные и удобные для пользователей.

Теги: