React use() — простое руководство по загрузке данных
Введение
React-приложения часто получают данные из внешних API. Традиционно для этого используют fetch() вместе с useEffect и useState, что приводит к повторяющемуся шаблонному коду. Новый хук use() упрощает эту задачу: он принимает промис или контекст и возвращает результат — либо «подвешивает» компонент до получения результата (через Suspense).
Определение: Suspense — встроенный механизм React, который приостановит рендер и покажет запасной UI (fallback) пока промис не завершится.
Важно: на момент написания use() является экспериментальным API и доступен в релизах React с тэгом “experimental”. Не используйте его в критичных продакшен-сборках без проверки совместимости.
Базовый компонент (традиционный подход)
Пример типичного компонента с useEffect/useState, который загружает данные по URL и обрабатывает загрузку и ошибку:
import {useEffect, useState} from "react"
export function Data({ url }) {
const [isLoading, setIsLoading] = useState(true)
const [isError, setIsError] = useState(false)
const [data, setData] = useState()
useEffect(() => {
setIsError(false)
setIsLoading(true)
setData(undefined)
fetch(url)
.then(res => res.json())
.then(setData)
.catch(() => setIsError(true))
.finally(() => setIsLoading(false))
})
return isLoading ? (
Loading...
) : isError ? (
Error
) : (
{JSON.stringify(data, null, 2)}
)
} Проблемы этого подхода: много повторяющегося кода, ручное управление состояниями, сложнее объединять данные с несколькими зависимостями.
Реализация с помощью use()
use() уменьшает объём шаблонного кода. Идея проста: передать в use() промис (или другой поддерживаемый источник), и React сам приостановит рендер до его разрешения.
Важно: вы должны использовать экспериментальную версию React в package.json:
// package.json
"dependencies": {
"react": "experimental",
"react-dom": "experimental"
} Затем импортируйте только use:
import {use} from "react"Пример упрощённого компонента Data:
export function Data({ url }) {
const data = use(fetch(url).then(res => res.json()))
return {JSON.stringify(data, null, 2)}
} Пояснение: если промис ещё не разрешился, React «подвесит» этот компонент. Родительский компонент должен оборачивать Data в Suspense и при необходимости показывать fallback UI.
Состояние загрузки через Suspense
Чтобы показать индикатор загрузки, поместите компонент в Suspense с пропом fallback:
import {Suspense, useState} from "react";
export default function App () {
const [url, setUrl] = useState(URL.USERS)
return (
<>
Loading... Когда Data вызывает use() с промисом, React покажет содержимое fallback до тех пор, пока промис не завершится. Это переносит логику загрузки из дочернего компонента в родительский UI.
Примечание: Suspense работает только с теми источниками, которые действительно «подвешивают» рендер (например, промисом, переданным в use()).
Обработка ошибок через Error Boundary
Для перехвата ошибок используйте Error Boundary. Он ловит ошибки во время рендера, в методах жизненного цикла и в конструкторах дочерних компонентов:
import React from "react"
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null }
static getDerivedStateFromError(error) {
return {
hasError: true,
error
}
}
render() {
if (this.state.hasError) {
return this.props.fallback
}
return this.props.children
}
}
export default ErrorBoundary; Оборачиваем App или часть дерева компонентами ErrorBoundary и Suspense вместе:
export default function App () {
const [url, setUrl] = useState(URL.USERS)
return (
<>
Упс! Произошла ошибка. Если в любом вложенном компоненте возникнет ошибка, Error Boundary переключит локальный UI на заданный fallback.
Правила использования use()
use() менее ограничен по месту вызова, чем классические хуки: он может быть вызван внутри if, циклов и т. п. Это даёт гибкость, но следует соблюдать осторожность.
Пример условного вызова:
export function Data({ url, shouldFetch }) {
let data = "Default data"
if (shouldFetch) {
const data = use(fetch(url).then(res => res.json()))
}
return {JSON.stringify(data, null, 2)}
} Вы можете также передать контекст в use():
export function Data({ url, shouldFetch }) {
let data = "Default data"
if (shouldFetch) {
const data = use(Context)
}
return {JSON.stringify(data, null, 2)}
} Однако будьте внимательны: динамические вызовы use() меняют модель рендеринга и усложняют предсказуемое поведение, если вы смешиваете его с обычными хуками в одном компоненте.
Important: смешивание динамического use() и классических хуков в одном компоненте может привести к запутанному коду. Держите код чистым и документируйте поведение.
Когда use() не годится или даёт проблемы
- Браузерная совместимость и релизы: use() экспериментален. В продакшене используйте только после проверки и наличия стратегии отката.
- Совместное использование с хуками, зависящими от порядка вызовов: если часть логики всё ещё требует строгого порядка хуков, динамическое размещение use() может нарушить ожидания.
- Параллельные запросы и кеширование: use() сам по себе не решает вопросы дедупликации запросов и кеширования; для сложных требований лучше использовать специализированные библиотеки.
Альтернативные подходы
- useEffect + useState: простой и полностью контролируемый способ (подходит для большинства случаев).
- React Query / TanStack Query: предоставляет кеш, дедупликацию, рефетчинг и SLR/CRR-подходы. Удобен для сложных сценариев с кэшированием и сайд-эффектами.
- SWR (Vercel): лёгкая библиотека для кеширования и рефетчинга, хороша для многих CRUD-случаев.
Выбор: use() хорош для упрощения рендера и тесной интеграции с Suspense. Для богатого клиентского кеша и сложных сценариев предпочтительнее React Query или SWR.
Модель мышления и эвристики
- Модель: data-as-suspense — вместо управления состоянием загрузки в каждом компоненте, вы передаёте промис и даёте родителю решить UI при задержке.
- Эвристики:
- Если у вас простой промис и вы хотите компактный код — используйте use().
- Если вам нужен кеш, дедупликация, retry и метрики — используйте React Query.
- Для кросс-компонентного доступа к данным (где нужен централизованный кеш) — внешний менеджер состояния предпочтительнее.
Практический чеклист перед внедрением use()
- Убедиться, что команда понимает экспериментальный статус API.
- Написать unit и интеграционные тесты для компонентов с Suspense и Error Boundary.
- Иметь план отката (вернуться на стабильную версию React).
- Документировать места, где use() вызывается динамически.
- Покрыть сценарии конкурентного рендера и параллельных запросов.
Критерии приёмки
- Компонент корректно показывает fallback при загрузке.
- При ошибке показывается ожидаемый UI из Error Boundary.
- Поведение совпадает на сервере и на клиенте при SSR (если применимо).
- Тесты на симуляцию задержек и ошибок проходят.
Тест-кейсы и проверки
- Симуляция долгого ответа: промис задерживается — приложение должно показать fallback.
- Симуляция ошибки: промис отклонён — Error Boundary должен показать fallback-ошибку.
- Условный fetch: передаём shouldFetch = false — компонент должен отобразить «Default data».
- Параллельные вызовы: несколько Data с одинаковым URL — ожидаем корректное поведение (в идеале дедупликация, если реализована).
Пример playbook для миграции (высокоуровнево)
- Идентифицируйте простые компоненты с повторяющимся useEffect/fetch.
- Напишите тесты поведения (загрузка/успех/ошибка).
- Переходите к экспериментальной ветке React в изолированной ветке сборки.
- Замените локальные реализации на use() по одному компоненту.
- Запустите интеграционные тесты и ручное тестирование.
- Проверяйте производительность и поведение SSR, если он используется.
- Документируйте и ретроспективно оценивайте риски.
Короткая заметка по безопасности и приватности
use() не изменяет требования к безопасности. Всегда валидируйте и очищайте данные, полученные из внешних API. При работе с персональными данными соблюдайте правила конфиденциальности и требования локального законодательства.
Быстрая шпаргалка (cheat sheet)
- Импорт: import {use} from “react”
- Передать промис: const data = use(fetch(url).then(r => r.json()))
- Обернуть в Suspense для показа fallback
- Для ошибок обернуть в Error Boundary
Цитата эксперта: “use() меняет акцент с управления состоянием загрузки на декларативное ожидание данных, но не заменяет менеджеры данных с кешем и политиками рефетчинга.”
Когда стоит подождать с использованием use()
- Если проект уже активно использует React Query и нужны все его преимущества.
- Если команда не готова к экспериментальным API и риску отката.
- Если необходима строгая совместимость с текущими инструментами SSR, сборки или мониторинга.
Итог
use() делает код компактнее и интегрируется со Suspense для упрощённой обработки состояния загрузки и Error Boundary для ошибок. Он отлично подходит для простых случаев загрузки данных и для декларативного стиля. В сложных сценариях, где важны кеширование, дедупликация и расширенное управление состоянием запроса, всё ещё лучше использовать проверенные библиотеки вроде React Query или SWR.
Summary:
- use() уменьшает шаблонный код и работает с промисами и контекстами.
- Suspense отвечает за UI при загрузке, Error Boundary — за UI при ошибке.
- На данный момент use() — экспериментальная функция; планируйте миграцию осторожно.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone