> Как работает обновление состояния при нескольких вызовах setState в React? (React, TypeScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Альфа-банк

Стек: React, TypeScript

> Пример ответа

Короткий ответ

При нескольких вызовах setState в одном синхронном контексте React объединяет (batches) обновления в один ререндер, используя последнее значение для каждого ключа состояния. В React 18 батчинг работает по умолчанию для всех вызовов, включая промисы и setTimeout. setState с функцией-апдейтером получает актуальное предыдущее состояние, а с объектом - может привести к потере промежуточных обновлений.

Подробное объяснение

React оптимизирует производительность, группируя множественные вызовы setState в один проход. Механизм батчинга работает следующим образом:

  • В синхронных обработчиках событий React (например, onClick) все вызовы setState собираются в очередь.
  • После завершения обработчика React выполняет единый ререндер, применяя все накопленные обновления.
  • Если передавать объект, каждый последующий вызов перезаписывает предыдущий для того же ключа: setCount(count + 1) трижды даст +1, а не +3, так как count на момент всех вызовов одинаков.
  • Функция-апдейтер setCount(prev => prev + 1) гарантирует актуальность, так как React передаёт в неё предыдущее значение из очереди.

В React 17 и ранее батчинг работал только внутри обработчиков событий. В React 18 он включён для всех контекстов: промисы, setTimeout, native events. Для отключения батчинга используется flushSync из react-dom.

На практике

В продакшене батчинг критичен для производительности: он предотвращает лишние ререндеры и пересчёты DOM. При работе с формами или анимациями, где важно промежуточное состояние, всегда используйте функциональную форму setState. Для сложной логики обновления (например, несколько полей формы) предпочтительнее один объект состояния и один setState с функцией-апдейтером.

В TypeScript функциональная форма лучше типизируется: setCount((prev: number) => prev + 1) - TypeScript выводит тип prev из объявления состояния.

Пример кода

TSX
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// Проблемный случай: три вызова с объектом
const handleObject = () => {
setCount(count + 1); // 0 + 1
setCount(count + 1); // 0 + 1 (count всё ещё 0)
setCount(count + 1); // 0 + 1 → итог: 1
};
// Правильный случай: функциональная форма
const handleFunctional = () => {
setCount(prev => prev + 1); // prev = 0 → 1
setCount(prev => prev + 1); // prev = 1 → 2
setCount(prev => prev + 1); // prev = 2 → 3
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleObject}>+1 (object)</button>
<button onClick={handleFunctional}>+3 (functional)</button>
</div>
);
}

Как отвечать на собеседовании

Начните с ключевого факта: React батчит обновления для производительности. Объясните разницу между объектом и функцией-апдейтером. Упомяните, что в React 18 батчинг расширен на асинхронные контексты. Приведите пример, когда функциональная форма спасает от багов. Если спросят про flushSync, покажите понимание его редкого использования. Избегайте углубления в реализацию fiber tree, если не просят.

Что проверяет интервьюер

  • Понимание механизма батчинга и его эволюции между версиями React.
  • Умение различать императивное (объект) и функциональное обновление состояния.
  • Знание практических последствий: потеря обновлений, производительность, типизация.
  • Осведомлённость о flushSync как инструменте отключения батчинга.
  • Способность объяснить концепцию на примере и без лишних деталей.

Типичные ошибки

  • Утверждение, что setState всегда асинхронен - на самом деле он синхронен в контексте батчинга, но ререндер отложен.
  • Использование объекта setState в циклах или асинхронных вызовах без учёта замыкания.
  • Игнорирование различий между React 17 и 18 в контексте промисов и setTimeout.
  • Путаница между батчингом и мемоизацией (useMemo, useCallback).
  • Предположение, что несколько setState в одном обработчике всегда дают несколько ререндеров.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью