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

Как React синхронизирует состояние DOM?

1.8 Middle🔥 231 комментариев
#JavaScript Core

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

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

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

Как React синхронизирует состояние с DOM

Это один из самых важных вопросов для понимания React. Ответ должен продемонстрировать знание реконсиляции, Virtual DOM и алгоритма обновления.

Общий процесс

React использует Virtual DOM и реконсиляцию для эффективной синхронизации состояния с реальным DOM:

Изменение состояния -> Рендер -> Virtual DOM -> Сравнение (Diffing) -> Обновление DOM

1. Инициирование обновления

Обновление может быть вызвано несколькими способами:

function Counter() {
  const [count, setCount] = React.useState(0);

  // Способ 1: setState (самый частый)
  const increment = () => setCount(count + 1);

  // Способ 2: Обновление props
  // Способ 3: useEffect
  // Способ 4: контекст

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Увеличить</button>
    </div>
  );
}

Когда вызовется setCount(count + 1), React не сразу обновляет DOM. Вместо этого он:

  1. Планирует обновление (добавляет в очередь)
  2. Объединяет множественные обновления (batching)
  3. Выполняет рендер компонента с новым состоянием

2. Рендер компонента

React вызывает функцию компонента с новым состоянием:

// До обновления
<div>
  <p>Count: 0</p>
  <button>Увеличить</button>
</div>

// После setCount(1), React вызывает функцию Counter() снова
// Теперь count = 1, функция возвращает новый JSX
<div>
  <p>Count: 1</p>
  <button>Увеличить</button>
</div>

Важное: это не меняет реальный DOM, это просто возвращает описание желаемого состояния.

3. Virtual DOM и Diffing

React создает Virtual DOM (объектное представление дерева):

// Старый Virtual DOM
{
  type: 'div',
  props: {},
  children: [
    { type: 'p', props: {}, children: 'Count: 0' },
    { type: 'button', props: {}, children: 'Увеличить' }
  ]
}

// Новый Virtual DOM
{
  type: 'div',
  props: {},
  children: [
    { type: 'p', props: {}, children: 'Count: 1' },  // <- изменилось
    { type: 'button', props: {}, children: 'Увеличить' }
  ]
}

Реакт сравнивает старый и новый Virtual DOM (это называется "reconciliation" или "diffing") и определяет минимальные изменения для реального DOM:

// Что нужно изменить:
// 1. Обновить текст внутри <p> с "0" на "1"
// Готово! Остальное не трогаем.

4. Обновление реального DOM

React применяет изменения к реальному DOM минимально:

// React делает примерно это (упрощенно):
const pElement = document.querySelector('p');
pElement.textContent = 'Count: 1'; // Только одно изменение

Вместо полной перестройки DOM, React обновляет только те узлы, которые изменились.

5. Reconciliation алгоритм

React использует умный алгоритм для определения, какие элементы переиспользовать, а какие создавать заново:

Правило 1: Элементы разных типов создают разные деревья

// Было
<div>Counter</div>

// Стало
<span>Counter</span>

// React удалит <div> и создаст <span> (полная переестройка)

Правило 2: Ключи (keys) помогают определить элементы

// Плохо: без keys
function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li>{item.name}</li>  // Опасно!
      ))}
    </ul>
  );
}

// Хорошо: с keys
function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>  // Безопасно
      ))}
    </ul>
  );
}

Ключи помогают React понять, какой элемент есть какой, особенно если порядок меняется.

Правило 3: Элементы одного типа сравниваются по props

// Было
<Input value="" onChange={handleChange} />

// Стало
<Input value="hello" onChange={handleChange} />

// React поймет, что это один и тот же Input, и обновит только value

Практический пример: как работает синхронизация

function UserProfile({ userId }) {
  const [user, setUser] = React.useState(null);
  const [posts, setPosts] = React.useState([]);

  React.useEffect(() => {
    // Загрузить пользователя
    fetchUser(userId).then(setUser);
    // Загрузить посты
    fetchPosts(userId).then(setPosts);
  }, [userId]);

  if (!user) return <div>Загрузка...</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            <strong>{post.title}</strong>
            <p>{post.content}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

// Процесс:
// 1. Компонент рендерится впервые: user = null, возвращает <div>Загрузка...</div>
// 2. useEffect запускается, делает API запросы
// 3. setUser и setPosts вызываются -> React планирует обновления
// 4. React батчит оба обновления в одно
// 5. Компонент рендерится снова с user и posts
// 6. Virtual DOM сравнивается: было "Загрузка...", стало профиль с постами
// 7. React минимально обновляет реальный DOM

Оптимизация синхронизации

1. Использование React.memo для предотвращения лишних рендеров

const UserCard = React.memo(function UserCard({ user }) {
  console.log('Рендер UserCard');
  return <div>{user.name}</div>;
});

// Если props не изменились, компонент не рендерится

2. useMemo для дорогостоящих вычислений

function ExpensiveList({ items }) {
  // Только пересчитываем, если items изменился
  const sortedItems = React.useMemo(
    () => items.sort((a, b) => a.name.localeCompare(b.name)),
    [items]
  );

  return <ul>{sortedItems.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}

3. useCallback для стабильных функций

function Form() {
  // handleSubmit не пересоздается каждый рендер
  const handleSubmit = React.useCallback(
    (e) => {
      e.preventDefault();
      // ...
    },
    [] // зависимости
  );

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

Fiber архитектура (React 16+)

В React 16+ используется Fiber — архитектура, которая позволяет:

  • Разбить работу на части (incremental rendering)
  • Прервать работу для срочных задач (urgent updates)
  • Переиспользовать выполненную работу (abort work)
  • Добавить приоритет различным типам работы
// React может прервать обновление, если нужно обработать ввод пользователя
// Это делает приложение более отзывчивым

Итоги

React синхронизирует состояние с DOM через:

  1. Планирование обновлений при изменении состояния
  2. Батчинг множественных обновлений
  3. Рендер компонента с новым состоянием
  4. Создание Virtual DOM (описание желаемого дерева)
  5. Diffing (сравнение старого и нового Virtual DOM)
  6. Минимальное обновление реального DOM (только измененные узлы)
  7. Reconciliation — определение, какие элементы переиспользовать

Это позволяет React быть очень эффективным даже с большими приложениями.

Как React синхронизирует состояние DOM? | PrepBro