← Назад к вопросам

Что такое act()?

2.0 Middle🔥 201 комментариев
#React#Тестирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое act()?

act() — это утилита, предоставляемая библиотеками для тестирования React-приложений (в основном React Testing Library и @testing-library/react), которая гарантирует, что все обновления компонента, связанные с состояниями, эффектами и промисами, будут обработаны и применены перед тем, как будут выполнены какие-либо утверждения (assertions) в тестах. Это критически важно для написания предсказуемых и стабильных тестов.

Основная проблема, которую решает act()

В React обновления интерфейса (рендеринг) происходят асинхронно. Когда в тесте вы вызываете действие, меняющее состояние (например, клик по кнопке, изменение пропсов, вызов сетевого запроса), React может не сразу обновить DOM. Без act() ваш тест может проверить состояние DOM до того, как React применит изменения, что приведёт к ложным срабатываниям или падению тестов.

Как работает act()

act() оборачивает код, который вызывает обновления в React, и гарантирует, что:

  • Все связанные с этим обновления завершены (включая повторные рендеры из-за изменения состояний или эффектов).
  • Отложенные эффекты (например, useEffect) выполнены.
  • Асинхронные операции (промисы, таймеры) разрешены.

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

1. Тестирование синхронного обновления состояния

import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Counter from './Counter';

test('increments counter on click', () => {
  render(<Counter />);
  const button = screen.getByRole('button', { name: /increment/i });
  
  // Оборачиваем пользовательское действие в act()
  act(() => {
    userEvent.click(button);
  });
  
  // После act() можно безопасно проверять обновлённый DOM
  expect(screen.getByText('1')).toBeInTheDocument();
});

2. Тестирование асинхронных операций

import { render, screen, act, waitFor } from '@testing-library/react';
import AsyncComponent from './AsyncComponent';

test('loads and displays data', async () => {
  render(<AsyncComponent />);
  
  // act() можно использовать с асинхронным кодом
  await act(async () => {
    await waitFor(() => {
      expect(screen.getByText('Data loaded')).toBeInTheDocument();
    });
  });
});

Когда act() НЕ нужен

Современные инструменты тестирования (как React Testing Library) автоматически оборачивают многие действия в act(). Например:

  • render() уже вызывает act().
  • userEvent.click(), fireEvent из RTL также обёрнуты в act().
  • Асинхронные утилиты типа waitFor, findBy* сами обрабатывают обновления.

Использовать act() вручную требуется редко, только в сложных сценариях, например:

  • При прямом вызове setState вне React-событий.
  • При работе с кастомными таймерами или промисами, не охваченными RTL.

Частая ошибка: предупреждение "An update was not wrapped in act(...)"

Это предупреждение возникает, когда обновление компонента происходит после завершения теста. Типичные причины:

  • Незавершённые промисы или таймеры.
  • Состояние, обновляемое после асинхронной операции, которую тест не дождался.

Решение — убедиться, что все асинхронные операции завершены, используя async/await, waitFor или правильное завершение в act().

Практические рекомендации

  1. По умолчанию не используйте act() напрямую — полагайтесь на встроенные методы RTL.
  2. Для асинхронных тестов используйте async/await с waitFor или findBy* запросами.
  3. Если появилось предупреждение — сначала проверьте, полностью ли тест ожидает всех обновлений.
  4. В сложных случаях (например, тестирование хуков) может потребоваться act() из react-dom/test-utils.

Итог

act() — это инструмент синхронизации тестов с асинхронной природой React. Он обеспечивает корректность тестов, но в современном стеке RTL его прямое использование минимизировано благодаря автоматическому оборачиванию ключевых операций. Понимание act() важно для отладки тестов и работы с нестандартными асинхронными сценариями.