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

Как React отслеживает изменения?

2.0 Middle🔥 251 комментариев
#React#Архитектура и паттерны

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

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

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

Как React отслеживает изменения

React отслеживает изменения используя несколько механизмов, основной из которых — Virtual DOM и сравнение объектов по ссылке (reference equality).

Virtual DOM и Reconciliation

Virtual DOM — это абстрактное представление реального DOM в памяти:

// Компонент выполняется и возвращает Virtual DOM
function Counter() {
  const [count, setCount] = React.useState(0)

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  )
}

// Virtual DOM выглядит примерно так:
{
  type: 'div',
  props: { children: [
    { type: 'p', props: { children: 'Count: 0' } },
    { type: 'button', props: { onClick: [...], children: 'Increment' } }
  ]}
}

Процесс отслеживания изменений

Шаг 1: Состояние меняется

setCount(1) // Меняем state

Шаг 2: Компонент перевыполняется

// React вызывает функцию компонента снова
// Новый Virtual DOM:
{
  type: 'div',
  props: { children: [
    { type: 'p', props: { children: 'Count: 1' } },
    { type: 'button', props: { onClick: [...], children: 'Increment' } }
  ]}
}

Шаг 3: Diffing (сравнение)

Олдый Virtual DOM              Новый Virtual DOM
  count: 0         =====>        count: 1

React сравнивает и находит разницу

Шаг 4: Reconciliation (синхронизация)

React обновляет только изменённые части реального DOM:

// Вместо обновления всего DOM
// React обновляет только <p> элемент
dom.querySelector('p').textContent = 'Count: 1'

Сравнение по ссылке (Reference Equality)

React использует === (строгое равенство) для сравнения:

// Примитивные типы
5 === 5           // true (всегда одно значение)
'hello' === 'hello' // true

// Объекты
{} === {}         // false (разные объекты в памяти)
const obj = {}
obj === obj       // true (один и тот же объект)

Это критично для React:

function Parent() {
  // ❌ Плохо: новый объект создаётся при каждом ререндере
  const handleClick = () => console.log('clicked')
  return <Child onClick={handleClick} />
}

// При каждом ререндере Parent
// Child видит НОВЫЙ handleClick (другая ссылка)
// и тоже ререндерится, хотя логика не изменилась

function Parent() {
  // ✅ Хорошо: функция стабильна
  const handleClick = useCallback(() => console.log('clicked'), [])
  return <Child onClick={handleClick} />
}

// Теперь handleClick имеет одну и ту же ссылку
// Child не ререндерится без необходимости

Механизм отслеживания State

При изменении state:

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

  // Первый рендер: count = 0
  // setCount(1) -> React планирует ререндер
  // Второй рендер: count = 1

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

React отслеживает изменение значения, не вызывает ререндер, если значение не изменилось:

setCount(0) // count уже 0
// React НЕ планирует ререндер (в некоторых режимах)

setCount(1) // count изменился
// React планирует ререндер

Props и Dependencies

React сравнивает props:

function Child({ name }) {
  return <div>{name}</div>
}

function Parent() {
  const [count, setCount] = useState(0)

  // Каждый рендер Parent
  // Child получает новый props объект {name: 'John'}
  return <Child name="John" />
}

// ВОПРОС: будет ли ререндер Child?
// React сравнивает props:
// {name: 'John'} === {name: 'John'} // false! разные объекты

Это нормально, потому что React сравнивает значения props, а не сами объекты:

const oldProps = { name: 'John' }
const newProps = { name: 'John' }

// React сравнивает:
oldProps.name === newProps.name // true, 'John' === 'John'

// Если Child обёрнут в React.memo:
const MemoChild = React.memo(Child)
// Он ререндерится только если props изменился

useEffect Dependencies

React отслеживает dependencies используя === сравнение:

function Counter() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('John')

  // Эффект запустится когда count изменится
  useEffect(() => {
    console.log('Count changed:', count)
  }, [count]) // зависимость

  // React сравнивает:
  // oldCount === newCount
  // 0 === 1 // true, выполнить эффект

  // Проблема с объектами:
  const user = { id: 1, name }
  useEffect(() => {
    console.log('User changed:', user)
  }, [user]) // Проблема!

  // При каждом ререндере
  // {id: 1, name: 'John'} !== {id: 1, name: 'John'}
  // Эффект выполняется каждый раз!

  // Решение: useMemo
  const user = useMemo(() => ({ id: 1, name }), [name])
  useEffect(() => {
    console.log('User changed:', user)
  }, [user])
}

Fiber Architecture

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

  1. Render Phase — React строит новый Virtual DOM
  2. Commit Phase — React обновляет реальный DOM

Это позволяет:

  • Pausе/resume работы
  • Приоритизировать обновления
  • Обрабатывать ошибки
// React может приостановить рендер
// если нужно обработать срочное событие

// Это улучшает responsiveness приложения

Как помочь React эффективнее отслеживать

1. Используй Key для списков

// ❌ Плохо: React не знает какой элемент какой
users.map((user) => <div>{user.name}</div>)

// ✅ Хорошо: React знает элемент по id
users.map((user) => <div key={user.id}>{user.name}</div>)

2. Стабилизируй объекты и функции

// ❌ Плохо
const handleClick = () => {}
const styles = { color: 'red' }

// ✅ Хорошо
const handleClick = useCallback(() => {}, [])
const styles = useMemo(() => ({ color: 'red' }), [])

3. Разделяй логику на мелкие компоненты

// ❌ Плохо: все в одном
function Page() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <Counter count={count} />
      <HeavyList items={items} /> // ререндерится при изменении count
    </div>
  )
}

// ✅ Хорошо: отделяем
function CounterSection() {
  const [count, setCount] = useState(0)
  return <Counter count={count} />
}
function Page() {
  return (
    <div>
      <CounterSection />
      <HeavyList items={items} /> // не ререндерится
    </div>
  )
}

Итоговое резюме

React отслеживает изменения через:

  1. Virtual DOM — абстрактное представление UI
  2. Diffing — сравнение старого и нового Virtual DOM
  3. Reconciliation — обновление реального DOM только где нужно
  4. Reference Equality (===) — сравнение значений для state, props, dependencies

Понимание этих механизмов помогает писать производительный код.