Хук use() в React 19

21 июля 2026
Автор

Антон Ларичев

Что такое хук use()

В React 19 появился новый встроенный хук use(), который принципиально отличается от всех остальных хуков. Он позволяет читать значение ресурса прямо во время рендера: будь то Promise или объект контекста (Context).

Главное отличие use() от привычных хуков — его можно вызывать условно: внутри if, циклов, вложенных функций. Это нарушает одно из базовых правил React-хуков, которое гласит «не вызывай хуки условно» — но для use() это правило специально не применяется.

import { use } from 'react';

function UserCard({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // React «приостановит» компонент до разрешения Promise

  return <div>{user.name}</div>;
}

Курс по теме

Освоить React и Redux Toolkit на курсе

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Использование use() с Promise

Базовый принцип работы

Когда use() получает Promise, React приостанавливает рендер компонента (аналогично механизму Suspense) и ждёт, пока промис не разрешится. После этого компонент рендерится заново уже с готовым значением.

Для корректной работы необходимо обернуть компонент в <Suspense>, чтобы показать пользователю состояние загрузки:

import { use, Suspense } from 'react';

interface Post {
  id: number;
  title: string;
  body: string;
}

function PostContent({ postPromise }: { postPromise: Promise<Post> }) {
  const post = use(postPromise);

  return (
    <article>
      <h2>{post.title}</h2>
      <p>{post.body}</p>
    </article>
  );
}

function PostPage({ id }: { id: number }) {
  const postPromise = fetch(`/api/posts/${id}`).then(res => res.json());

  return (
    <Suspense fallback={<div>Загрузка...</div>}>
      <PostContent postPromise={postPromise} />
    </Suspense>
  );
}

Важно: создавайте Promise вне компонента

Передача промиса, созданного прямо при рендере родителя, приведёт к бесконечной перезагрузке — каждый рендер создаёт новый промис. Правильный подход — создавать промис вне компонента или кэшировать его:

import { use, Suspense, useState } from 'react';

// Правильно: промис создаётся один раз вне компонента
const userPromise = fetch('/api/user/1').then(res => res.json());

function UserProfile() {
  const user = use(userPromise);
  return <p>Привет, {user.name}!</p>;
}

// Или через состояние родителя, которое не меняется при ре-рендере
function App() {
  const [postPromise] = useState(() =>
    fetch('/api/posts/1').then(res => res.json())
  );

  return (
    <Suspense fallback={<p>Загрузка поста...</p>}>
      <PostContent postPromise={postPromise} />
    </Suspense>
  );
}

Обработка ошибок через Error Boundary

Если промис отклоняется (rejected), use() выбрасывает ошибку. Её нужно перехватить с помощью Error Boundary:

import { use, Suspense, Component } from 'react';

class ErrorBoundary extends Component<
  { children: React.ReactNode; fallback: React.ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}

function UserData({ promise }: { promise: Promise<User> }) {
  const user = use(promise);
  return <span>{user.name}</span>;
}

function App() {
  const userPromise = fetchUser(1);

  return (
    <ErrorBoundary fallback={<p>Не удалось загрузить данные</p>}>
      <Suspense fallback={<p>Загрузка...</p>}>
        <UserData promise={userPromise} />
      </Suspense>
    </ErrorBoundary>
  );
}

Использование use() с Context

use() может заменять useContext(), но с важным преимуществом: его можно вызывать внутри условных выражений.

import { use, createContext } from 'react';

const ThemeContext = createContext<'light' | 'dark'>('light');

function ThemeIcon({ showTheme }: { showTheme: boolean }) {
  // useContext нельзя вызвать здесь условно, use() — можно
  if (showTheme) {
    const theme = use(ThemeContext);
    return <span>Тема: {theme}</span>;
  }

  return null;
}

Сравнение useContext и use

import { useContext, use, createContext } from 'react';

const AuthContext = createContext<{ isAdmin: boolean } | null>(null);

// Старый подход — нельзя вызывать условно
function AdminPanel_Old({ showPanel }: { showPanel: boolean }) {
  const auth = useContext(AuthContext); // всегда вызывается, даже если не нужен

  if (!showPanel) return null;

  return auth?.isAdmin ? <div>Панель администратора</div> : null;
}

// Новый подход с use() — вызываем только когда нужно
function AdminPanel_New({ showPanel }: { showPanel: boolean }) {
  if (!showPanel) return null;

  const auth = use(AuthContext); // вызываем только при showPanel === true
  return auth?.isAdmin ? <div>Панель администратора</div> : null;
}

Паттерн: передача Promise сверху вниз

Один из ключевых паттернов React 19 — поднять создание промиса в родительский компонент и передавать его дочерним через пропсы. Это позволяет начать загрузку данных как можно раньше:

import { use, Suspense, useState } from 'react';

interface Comment {
  id: number;
  text: string;
  author: string;
}

interface Article {
  title: string;
  content: string;
}

async function fetchArticle(id: number): Promise<Article> {
  const res = await fetch(`/api/articles/${id}`);
  if (!res.ok) throw new Error('Статья не найдена');
  return res.json();
}

async function fetchComments(articleId: number): Promise<Comment[]> {
  const res = await fetch(`/api/articles/${articleId}/comments`);
  if (!res.ok) throw new Error('Комментарии недоступны');
  return res.json();
}

function ArticleBody({ articlePromise }: { articlePromise: Promise<Article> }) {
  const article = use(articlePromise);
  return (
    <div>
      <h1>{article.title}</h1>
      <p>{article.content}</p>
    </div>
  );
}

function CommentList({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise);
  return (
    <ul>
      {comments.map(c => (
        <li key={c.id}>
          <strong>{c.author}:</strong> {c.text}
        </li>
      ))}
    </ul>
  );
}

// Оба запроса стартуют параллельно — Promise создаётся в родителе
function ArticlePage({ articleId }: { articleId: number }) {
  const [articlePromise] = useState(() => fetchArticle(articleId));
  const [commentsPromise] = useState(() => fetchComments(articleId));

  return (
    <div>
      <Suspense fallback={<p>Загрузка статьи...</p>}>
        <ArticleBody articlePromise={articlePromise} />
      </Suspense>
      <Suspense fallback={<p>Загрузка комментариев...</p>}>
        <CommentList commentsPromise={commentsPromise} />
      </Suspense>
    </div>
  );
}

Благодаря тому, что оба промиса создаются в ArticlePage одновременно, запросы идут параллельно — нет «водопада» (waterfall), как это случалось при использовании useEffect.

Сравнение с useEffect

Традиционный подход с useEffect порождает дополнительные состояния, условный рендер и «мигание» UI:

// Старый подход с useEffect
function UserCard_Old({ userId }: { userId: number }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err);
        setLoading(false);
      });
  }, [userId]);

  if (loading) return <p>Загрузка...</p>;
  if (error) return <p>Ошибка: {error.message}</p>;
  if (!user) return null;

  return <div>{user.name}</div>;
}

// Новый подход с use()
function UserCard_New({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // чисто и прямолинейно
  return <div>{user.name}</div>;
}

Логику загрузки и ошибок use() делегирует Suspense и ErrorBoundary — компонент остаётся чистым и отвечает только за отображение данных.

Ограничения хука use()

Только клиентские и серверные компоненты

use() работает в обоих типах компонентов, но семантика отличается. В серверных компонентах React лучше использовать async/await напрямую, так как серверный рендер не имеет понятия «приостановить и возобновить»:

// Серверный компонент — используй async/await
async function ServerUserCard({ userId }: { userId: number }) {
  const user = await fetchUser(userId); // предпочтительно на сервере
  return <div>{user.name}</div>;
}

// Клиентский компонент — используй use()
'use client';
function ClientUserCard({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // подходит для клиента
  return <div>{user.name}</div>;
}

use() нельзя использовать внутри try/catch

function Component({ promise }: { promise: Promise<Data> }) {
  // Не делай так — это не работает
  try {
    const data = use(promise);
    return <div>{data.value}</div>;
  } catch (e) {
    return <div>Ошибка</div>;
  }

  // Правильно: используй ErrorBoundary снаружи
}

Ошибки из use() должны перехватываться Error Boundary-компонентом, а не try/catch внутри функции рендера.

Практический пример: поиск с use()

import { use, Suspense, useState, useTransition } from 'react';

interface SearchResult {
  id: number;
  name: string;
}

function searchProducts(query: string): Promise<SearchResult[]> {
  return fetch(`/api/search?q=${encodeURIComponent(query)}`)
    .then(res => res.json());
}

function SearchResults({ resultsPromise }: { resultsPromise: Promise<SearchResult[]> }) {
  const results = use(resultsPromise);

  if (results.length === 0) {
    return <p>Ничего не найдено</p>;
  }

  return (
    <ul>
      {results.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

function SearchPage() {
  const [query, setQuery] = useState('');
  const [resultsPromise, setResultsPromise] = useState<Promise<SearchResult[]>>(
    () => Promise.resolve([])
  );
  const [isPending, startTransition] = useTransition();

  function handleSearch(e: React.ChangeEvent<HTMLInputElement>) {
    const value = e.target.value;
    setQuery(value);

    startTransition(() => {
      // useTransition откладывает обновление промиса,
      // пока не будут готовы новые данные
      setResultsPromise(searchProducts(value));
    });
  }

  return (
    <div>
      <input
        value={query}
        onChange={handleSearch}
        placeholder="Поиск товаров..."
      />
      {isPending && <p>Поиск...</p>}
      <Suspense fallback={<p>Загрузка результатов...</p>}>
        <SearchResults resultsPromise={resultsPromise} />
      </Suspense>
    </div>
  );
}

Использование useTransition вместе с use() — рекомендуемый паттерн: переход считается «неприоритетным», и React не блокирует UI на время ожидания нового промиса.

Итог

Хук use() — это фундаментальное изменение в модели работы с асинхронными данными в React:

  • Позволяет читать Promise прямо во время рендера без useEffect и промежуточных состояний
  • Может вызываться условно, в отличие от других хуков
  • Заменяет useContext() там, где нужна условная читаемость контекста
  • Делегирует состояния загрузки и ошибок Suspense и Error Boundary
  • Хорошо работает в связке с useTransition для плавной смены данных

Для освоения React 19 и всех новых возможностей — включая серверные компоненты, Server Actions и обновлённую модель работы с данными — смотри курс на PurpleSchool: https://purpleschool.ru/course/react?utm_source=knowledgebase&utm_medium=text&utm_campaign=use-hook-react-19

Стрелочка влевоuseMemo в React: как и когда оптимизировать тяжелые вычисленияuseEffect в React что это и как использоватьСтрелочка вправо

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

Uncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг React-кода: техники и лучшие практикиПрофилирование React: как найти и устранить узкие местаЧастичное применение: как создавать компоненты без лишнего кодаИменование компонентов в React: соглашения и лучшие практикиЛенивая загрузка: как ускорить React-приложение в разыHOC в React: мастерство композиции компонентовuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияCompound Components в React: создаем гибкие компоненты с мощным APIКонтролируемые компоненты в React: полный контроль над формамиДокументирование компонентов в React: Storybook, JSDoc и READMEКомпозиция компонентов в React: строим гибкие интерфейсыКомментирование кода в React: когда и как писать комментарииCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с даннымиДоступность (a11y) в React: ARIA, семантика и клавиатурная навигация
Zustand — управление состоянием в ReactZod - валидация с TypeScriptYup - валидация схемXState - конечные автоматыТемизация в ReactТестирование хуковTailwind CSS с ReactStyled Components — стилизация через JSSWR - библиотека для запросовStorybook - документация компонентовSnapshots тестированиеRTK Query - работа с APIRedux Toolkit - современный ReduxRecoil — библиотека управления состоянием от FacebookВиртуализация списков с react-window: как отображать тысячи элементов без лаговReact Toastify - уведомления в ReactReact Testing LibraryСоздание таблиц в React гайд по react-tableReact Spring - анимацииРабота с формами и селектами в ReactReact Query (TanStack Query) - работа с серверомПлагины в React что это и как их использоватьReact PDF - работа с PDF файламиОбзор популярных библиотек для ReactReact Icons - библиотека иконок для ReactReact Hook Form — валидация форм в ReactReact Dropzone — загрузка файловПодключение Bootstrap к React-приложениюReact Beautiful DnD - перетаскивание элементовАнимация при монтировании компонентов в ReactМокирование APIMobX — реактивное управление состоянием в ReactМикрофронтенды с React (micro-frontends)Загрузка и индикаторыАнимация списков в ReactJotai - атомарное состояниеБесконечная прокруткаFramer Motion - библиотека анимацийEmotion — библиотека CSS-in-JSДинамические стили в ReactE2E тестирование с CypressCSSTransition - переходыCSS-in-JS — плюсы и минусыКонтекст vs Redux — когда что использоватьИспользование Chart.js в ReactAxios с ReactТестирование асинхронных компонентовОбработка ошибок API
useState в React что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияХук use() в React 19useEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextНовые возможности React 19Как предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
Transition API — плавные обновления интерфейса в ReactReact Suspense — приостановка рендераStrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMFragment в React: группировка элементов без лишних узлов DOMCSS Modules в ReactConcurrent Mode — конкурентный режим в React
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий