Назад
Назад к заметкам
· 4 мин чтения

use новый хук в React 19: практические приемы

Хук use объединяет состояние и побочный эффект в React 19, сокращая код и устраняя лишние ререндеры. Узнайте, как правильно его использовать, избежать бесконечных обновлений и писать тесты.

reacthooksperformanceasynchronous

Если в проекте на React 18 вы привыкли писать useState + useEffect парами, после перехода на React 19 ваш код может выглядеть раздутым. Новый инструмент use объединяет состояние и эффект в одной декларации, избавляя от лишних синхронизаций.

В этой статье разберем, как заставить use работать без сюрпризов, какие подводные камни есть у синхронных и асинхронных вызовов, и какие приемы помогут держать рендер-цикл под контролем.


Что дает хук use: объединение состояния и эффекта

use объявляет “состояние-привязанное действие” в одном месте. Вместо:

const [data, setData] = useState(null);
useEffect(() => {
  fetch(url)
    .then((r) => r.json())
    .then(setData);
}, [url]);

получаем компактный вариант:

const data = use(url, async (u) => {
  const r = await fetch(u);
  return r.json();
});
  • use принимает два аргумента: ключ (зависимость) и функцию-обработчик.
  • Возвращаемое значение уже находится в состоянии, его достаточно лишь читать.

Сокращенный синтаксис сразу уменьшает количество строк и устраняет двойной setState-цикл. Для привычных к useEffect разработчиков use выглядит как “один-в-один” паттерн с более явным потоком данных.


Синтаксис и типы параметров

ПараметрОписаниеПример
keyЗначение, от которого зависит переиспользование. Может быть примитивом или массивом.userId const userId = props.id;
fnФункция, вызываемая при изменении key. Возвращает промис или синхронное значение.async id => { const resp = await fetchUser(id); return resp.data; }
options(необязательно)Объект с флагамиlazy, debounce, resetOnUnmount.{ lazy: true, debounce: 300 }

Типы проверяются TypeScript-ом. Если fn возвращает Promise<T>, переменная будет иметь тип T | undefined до завершения. Это помогает избежать ошибок “null-reference”.


Как избежать бесконечного цикла обновлений

Самая частая ошибка использовать внутри fn переменную, меняющуюся каждый рендер. Например:

const [filter, setFilter] = useState("");
const result = use(filter, () => heavyCalc(filter, Math.random()));

Math.random() генерирует новое значение при каждом рендере, и use воспринимает его как новую зависимость. Это приводит к бесконечным вызовам.

Решение: вынести “нестабильные” значения в options или мемоизировать их.

const stableSeed = useMemo(() => Math.random(), []);
const result = use(filter, () => heavyCalc(filter, stableSeed));

Теперь stableSeed фиксируется один раз, а use перезапускается только при изменении filter. Такой подход избавляет от лишних перезапусков и делает производительность предсказуемой.


Работа с асинхронными данными

use автоматически управляет статусом загрузки. Расширенный вариант useAsync возвращает data, error и loading.

const { data, loading, error } = useAsync(userId, async (id) => {
  const res = await fetch(`/api/user/${id}`);
  if (!res.ok) throw new Error("Network error");
  return res.json();
});

if (loading) return <Spinner />;
if (error) return <ErrorMessage err={error} />;
return <UserCard user={data} />;

use отменяет запрос при размонтировании, предотвращая ошибки “setState on unmounted component”. Подробнее о отмене запросов читайте в статье о виртуализация списка.


Тестирование и отладка use

Тестировать хук проще, чем два отдельных (useState + useEffect). Библиотека @testing-library/react-hooks (или ее современный аналог) позволяет написать:

import { renderHook, act } from "@testing-library/react-hooks";
import { use } from "react";

test("fetches data on key change", async () => {
  const mockFetch = jest.fn(async (id) => ({ id, name: "User" + id }));
  const { result, waitForNextUpdate } = renderHook(() => use(1, mockFetch));

  expect(result.current).toBeUndefined();
  await waitForNextUpdate();
  expect(result.current).toEqual({ id: 1, name: "User1" });
});

В случае ошибок use бросает исключения, которые легко перехватить в тесте. Для профайлинга включите React.StrictMode и просмотрите сообщения в консоли. Если возникают неожиданные перерендеры, обратитесь к рекомендациям по оптимизации рендера.


FAQ / чеклист

  • Не включайте в fn переменные, изменяющиеся каждый рендер. Меморизируйте их через useMemo.
  • Используйте lazy-опцию, если запрос дорогой и не нужен сразу. Это откладывает загрузку до момента реального обращения.
  • Обрабатывайте ошибки внутри fn. Исключения пробрасываются в компонент.
  • Отменяйте запросы при размонтировании. use делает это автоматически, но ваш fetch должен поддерживать AbortController.
  • Не забывайте про типизацию. Явно указывайте тип возвращаемого значения функции, иначе получите any.

Новый хук use удобен, когда нужен быстрый способ связать состояние с побочным эффектом без лишнего boilerplate.

Он особенно полезен в формах, загрузке данных и небольших UI-виджетах, где каждый ререндер стоит дорого. Применяйте предложенные практики и ваш код станет чище, а приложение быстрее.

Больше информации о React 19 найдете в статье React 19: что нового.

Готов начать?

Выбери удобный способ связи — напиши напрямую или оставь заявку

Написать в Telegram

Отвечу в течение пары часов