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

Зачем React ожидает выполнения всех действий пользователя для рендера?

1.7 Middle🔥 232 комментариев
#React

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Концепция батчинга событий в React

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

Что такое батчинг?

Батчинг — это оптимизация, при которой React объединяет несколько обновлений состояния в единый рендер вместо отдельного рендера для каждого обновления. Это критически важно для производительности.

Почему это нужно?

1. Производительность:

// БЕЗ батчинга (старое поведение):
setCount(1);    // Рендер 1
setName(John); // Рендер 2
setAge(30);      // Рендер 3
// Итого: 3 рендера

// С батчингом (новое поведение):
setCount(1);     // }
setName(John); // } Объединены в 1 рендер
setAge(30);      // }

Каждый рендер требует пересчёта и обновления DOM — это дорогая операция. Батчинг снижает количество перерисовок.

2. Предсказуемость:

Без батчинга разные части UI обновлялись бы в разное время, что может привести к несогласованности состояния на экране.

// Пример потенциальной проблемы без батчинга:
const handleClick = () => {
  setIsLoading(true);   // Рендер 1 - экран показывает спиннер
  setData(newData);     // Рендер 2 - экран обновляет данные
  setIsLoading(false);  // Рендер 3 - экран убирает спиннер
  // Пользователь видит все 3 состояния отдельно
};

Как батчинг работает?

В React 18+ батчинг работает автоматически:

import { useState } from react;

function Component() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState();

  const handleClick = () => {
    // Все эти обновления батчированы в один рендер
    setCount(prev => prev + 1);
    setName(Alice);
  };

  return <button onClick={handleClick}>Click me</button>;
}

Даже асинхронные обновления в некоторых случаях батчируются:

const handleAsync = async () => {
  const data = await fetchData();
  setData(data);    // }
  setLoading(false); // } Батчируются вместе
};

Отключение батчинга: flushSync()

В редких случаях нужно принудительно отключить батчинг и обновить DOM сразу:

import { flushSync } from react-dom;

const handleClick = () => {
  flushSync(() => {
    setCount(prev => prev + 1);  // Рендер происходит СРАЗУ
  });
  console.log(document.getElementById(count).textContent); // Получим новое значение
};

React 17 vs React 18

React 17:

  • Батчинг только внутри обработчиков событий
  • Асинхронный код (setTimeout, Promises) батчируется отдельно

React 18:

  • Автоматический батчинг ВЕЗДЕ (события, асинхронный код, микротаски)
  • Можно отключить только через flushSync()

Практический пример

function Form() {
  const [email, setEmail] = useState();
  const [password, setPassword] = useState();
  const [errors, setErrors] = useState({});

  const handleSubmit = (e) => {
    e.preventDefault();
    
    // Все три setState вызова объединены в один рендер
    setEmail();      // Очищаем форму
    setPassword();
    setErrors({});     // Очищаем ошибки
    
    // Пользователь видит одно обновление, а не три отдельных
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

Выводы

  • React батчирует обновления для оптимизации производительности
  • Уменьшает количество рендеров и обновлений DOM
  • В React 18+ работает автоматически везде
  • Можно отключить через flushSync() в редких случаях
  • Это делает приложение более предсказуемым и быстрым