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

Как устроен рендеринг в React.js?

2.2 Middle🔥 281 комментариев
#React

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

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

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

Рендеринг в React.js: Полный механизм

Что такое рендеринг

Рендеринг — это процесс преобразования компонентов React в реальные элементы DOM, которые видит пользователь. React использует сложный механизм для оптимизации этого процесса.

Фазы рендеринга

1. Фаза рендера (Render Phase)

Реакт анализирует изменения и подготавливает обновления:

function MyComponent({ count }) {
  console.log("Render phase"); // Вызывается каждый раз
  return <div>{count}</div>;
}

// Эта функция вызывается, но изменения НЕ применены на DOM

Что происходит:

  • React вызывает функцию компонента
  • Получает новый Virtual DOM (JSX структура)
  • Сравнивает с предыдущим Virtual DOM (reconciliation)
  • Определяет, какие изменения нужны

2. Фаза коммита (Commit Phase)

Реакт применяет все изменения на реальный DOM:

function MyComponent({ count }) {
  useLayoutEffect(() => {
    console.log("Commit phase: DOM обновлён");
  }, [count]);
  
  return <div>{count}</div>;
}

Процесс Reconciliation (Согласование)

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

// React сравнивает два дерева
const oldVDom = <div><span key="1">A</span></div>;
const newVDom = <div><span key="1">B</span></div>;

// Только текст изменится, структура остается
// React НЕ пересоздает элемент

Fiber архитектура

В React 16+ используется Fiber — улучшенный алгоритм рендеринга:

// Fiber разбивает большие обновления на части
// Это позволяет браузеру обрабатывать другие задачи

function HeavyComponent() {
  const [items, setItems] = useState([]);
  
  const handleClick = () => {
    // React разделит это на небольшие chunks
    setItems(Array(1000).fill(0).map((_, i) => i));
  };
  
  return (
    <div>
      <button onClick={handleClick}>Загрузить 1000 элементов</button>
      {items.map(item => <div key={item}>{item}</div>)}
    </div>
  );
}

useState Батчинг (State Batching)

function Counter() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  
  const handleClick = () => {
    // React 18: оба setState вызываются в одном рендере
    setCount(c => c + 1);
    setText("clicked");
    // Только ОДИН рендер вместо двух
  };
  
  return <button onClick={handleClick}>{count}</button>;
}

Асинхронный рендеринг (Concurrent Rendering)

React 18+ может прерывать рендеринг для более важных обновлений:

function SearchComponent() {
  const [search, setSearch] = useState("");
  const [results, setResults] = useState([]);
  
  const handleChange = (e) => {
    // Ввод текста (высокий приоритет)
    setSearch(e.target.value);
    
    // Результаты поиска (низкий приоритет)
    startTransition(() => {
      setResults(expensiveSearch(e.target.value));
    });
  };
  
  return (
    <input value={search} onChange={handleChange} />
  );
}

useEffect и побочные эффекты

function MyComponent() {
  // Рендер фаза (Render phase)
  const [count, setCount] = useState(0);
  console.log("Render:", count);
  
  // Коммит фаза (Commit phase)
  useLayoutEffect(() => {
    console.log("LayoutEffect (синхронно)");
    // Вызывается ДО отрисовки браузером
  }, [count]);
  
  useEffect(() => {
    console.log("Effect (асинхронно)");
    // Вызывается ПОСЛЕ отрисовки браузером
  }, [count]);
  
  return <div onClick={() => setCount(c => c + 1)}>{count}</div>;
}

// Порядок вывода:
// 1. Render: 0
// 2. LayoutEffect (синхронно)
// 3. Браузер отрисовывает DOM
// 4. Effect (асинхронно)

Оптимизация рендеринга

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

function ExpensiveComponent({ items }) {
  const sortedItems = useMemo(
    () => items.sort((a, b) => a - b),
    [items]
  );
  
  return <div>{sortedItems.join(", ")}</div>;
}

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

function Parent() {
  const handleClick = useCallback(() => {
    console.log("Клик");
  }, []); // Функция не пересоздается
  
  return <Child onClick={handleClick} />;
}

React.memo для предотвращения ненужных рендеров

const MemoChild = React.memo(function Child({ data }) {
  console.log("Child render");
  return <div>{data}</div>;
});

function Parent() {
  const [count, setCount] = useState(0);
  
  return (
    <>
      <MemoChild data="static" />
      <button onClick={() => setCount(c => c + 1)}>{count}</button>
    </>
  );
  // MemoChild не перерисуется, так как data не изменилась
}

Ключи (Keys) в списках

// ❌ Плохо: используешь индекс как ключ
function List({ items }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li> // Проблемы при сортировке
      ))}
    </ul>
  );
}

// ✅ Хорошо: уникальный идентификатор
function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Итоги

Рендеринг в React состоит из:

  1. Render phase — подготовка изменений
  2. Commit phase — применение на DOM
  3. Fiber architecture — разбиение работы на chunks
  4. Reconciliation — умное сравнение Virtual DOM
  5. Concurrent rendering — приоритизация обновлений

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