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

Сколько будет перерисовок, если внутри useEffect вызвать 3 setState?

2.0 Middle🔥 241 комментариев
#JavaScript Core

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

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

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

Анализ перерисовок при вызове нескольких setState в useEffect

Этот вопрос затрагивает ключевые аспекты работы React: батчинг обновлений состояния, жизненный цикл компонентов и механизм ререндеров. Ответ зависит от контекста и версии React.

Батчинг в React 17 и 18

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

Пример кода:

import { useState, useEffect } from 'react';

function Component() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');
  const [flag, setFlag] = useState(false);

  useEffect(() => {
    console.log('Effect running');
    setCount(1);      // Первый setState
    setText('hello'); // Второй setState
    setFlag(true);    // Третий setState
  }, []);

  console.log('Render called');
  return <div>Count: {count}, Text: {text}, Flag: {flag.toString()}</div>;
}

Количество перерисовок

  1. В React 17 и более ранних версиях (внутри большинства обработчиков):

    • Все три вызова setState внутри useEffect будут забатчены.
    • Произойдет одна перерисовка после завершения эффекта.
    • Общий счет: Первоначальный рендер + один ререндер = 2 рендера.
  2. В React 18 с Concurrent Features (при использовании createRoot):

    • Батчинг работает по умолчанию во всех местах (включая таймауты, промисы, нативные обработчики).
    • Также будет одна перерисовка.
    • Общий счет: 2 рендера (аналогично).

Детальный процесс

1. Первый рендер (mount):
   - Компонент монтируется
   - useEffect регистрируется
   - Консоль: "Render called"

2. Фаза эффектов:
   - Выполняется callback useEffect
   - Консоль: "Effect running"
   - Три setState ставятся в очередь обновлений

3. Фаза ререндера:
   - React обрабатывает все накопленные обновления состояния
   - Один ререндер со всеми новыми значениями
   - Консоль: "Render called" (второй раз)

Исключения и важные нюансы

  1. Асинхронные операции внутри useEffect:

    useEffect(() => {
      setTimeout(() => {
        setCount(1);      // Эти вызовы НЕ будут забатчены
        setText('hello'); // в React 17, но будут в React 18
        setFlag(true);
      }, 1000);
    }, []);
    
  2. Множественные эффекты: Если у вас несколько эффектов, каждый вызывает setState:

    useEffect(() => { setCount(1); }, []);
    useEffect(() => { setText('hello'); }, []);
    useEffect(() => { setFlag(true); }, []);
    
    • В этом случае React попытается их забатчить, но результат зависит от внутренней реализации.
    • Обычно все равно происходит один ререндер.
  3. Потенциальные дополнительные ререндеры:

    • Если родительский компонент ререндерится, это может вызвать дополнительный ререндер дочернего.
    • Использование React.memo или useMemo может изменить поведение.

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

  • Используйте функциональные обновления, если новое состояние зависит от предыдущего:

    setCount(prev => prev + 1);
    setCount(prev => prev + 1); // Корректная работа с предыдущим состоянием
    
  • Группируйте связанные состояния в один объект, если они часто обновляются вместе:

    const [state, setState] = useState({ count: 0, text: '', flag: false });
    // Одно обновление вместо трех
    setState(prev => ({ ...prev, count: 1, text: 'hello', flag: true }));
    
  • Помните про Strict Mode в разработке: компоненты рендерятся дважды для обнаружения побочных эффектов.

Итог

В стандартном сценарии (синхронные вызовы setState внутри useEffect) будет:

  • Один дополнительный ререндер (всего 2 рендера)
  • Независимо от количества setState (3, 5, 10) - React батчит их в одно обновление
  • Исключение: React 17 и асинхронные операции (таймауты, промисы) могут вызвать несколько ререндеров

Оптимизация батчинга - одна из причин, почему вызов нескольких setState подряд не приводит к проблемам с производительностью в большинстве случаев.

Сколько будет перерисовок, если внутри useEffect вызвать 3 setState? | PrepBro