React Front-end Инженер

React Front-end Инженер

Роадмап навыков для прокачки

Что такое Fiber в React

ReactReconciliationFiber

Основная идея

Fiber — это новая архитектура ядра React, представленная в версии 16. Это переписанный с нуля алгоритм reconciliation, который позволяет делать рендеринг прерываемым и приоритетным.

Ключевые аспекты

  • Fiber-нода — JavaScript-объект, представляющий компонент или DOM-элемент
  • Unit of work — единица работы, которую React может выполнить и приостановить
  • Прерываемость — возможность остановить рендер и продолжить позже
  • Приоритеты — срочные обновления выполняются раньше менее важных
  • Два дерева — current (на экране) и work-in-progress (строится)

Для чего нужен Fiber

  • Плавные анимации при сложных обновлениях
  • Отзывчивый UI даже при больших деревьях компонентов
  • Возможность реализации Suspense и Concurrent Mode
  • Улучшенная обработка ошибок (Error Boundaries)

Частые ошибки на собеседованиях

  • Путают Fiber и Virtual DOM — Fiber это способ работы с Virtual DOM
  • Думают, что Fiber появился в React 18 — он появился в React 16
  • Считают, что Fiber автоматически делает всё асинхронным — нужен Concurrent Mode
  • Не различают Fiber как архитектуру и Fiber-ноду как структуру данных

Введение и проблематика

Что было до Fiber?

До React 16 использовался Stack Reconciler — синхронный алгоритм, который обновлял дерево компонентов рекурсивно. Главная проблема: если дерево большое, браузер "зависал" на время обновления.

text
// Типичная ситуация до Fiber:
Пользователь печатает → setState → Рендер 100мс → UI замирает

Браузер рисует 60 кадров в секунду, то есть каждые ~16мс. Если JavaScript работает дольше, пользователь видит "подтормаживания".

Решение: Fiber

React 16 представил полностью переписанное ядро — Fiber. Это не просто оптимизация, а фундаментально новая архитектура.


Базовая теория

Определение Fiber

Fiber — это:

  1. Архитектура — новый способ организации работы React
  2. Структура данных — объект, представляющий единицу работы
  3. Реализация reconciliation — алгоритма сравнения деревьев

Fiber-нода

Каждый элемент в React-приложении представлен Fiber-нодой:

Code Example 1: Какие основные поля содержит Fiber-нода? Объясните назначение полей child, sibling, return и alternate.

ts
interface Fiber {
  // === Идентификация ===
  type: any;                 // 'div', MyComponent, Fragment
  key: string | null;
 
  // === Дерево (linked list) ===
  child: Fiber | null;       // первый ребёнок
  sibling: Fiber | null;     // следующий "брат"
  return: Fiber | null;      // родитель
 
  // === Состояние ===
  pendingProps: any;         // входящие props
  memoizedProps: any;        // props после рендера
  memoizedState: any;        // state после рендера
 
  // === Эффекты ===
  flags: number;             // Placement | Update | Deletion
 
  // === Double buffering ===
  alternate: Fiber | null;   // ссылка на "другую версию"
}

Типы Fiber-нод

graph TD A[Fiber Types] --> B[HostRoot] A --> C[FunctionComponent] A --> D[ClassComponent] A --> E[HostComponent] A --> F[HostText] B --> B1["корень приложения"] C --> C1["function App() {}"] D --> D1["class App extends React.Component"] E --> E1["div, span, button..."] F --> F1["текстовые ноды"]

Ключевые концепции

1. Единица работы (Unit of Work)

В отличие от рекурсивного обхода, Fiber позволяет обрабатывать по одной ноде за раз:

Code Example 2: Как Fiber позволяет разбивать работу на части? Что происходит, когда "время вышло"?

js
// Псевдокод работы Fiber
while (есть_работа && есть_время) {
  текущая_нода = выполнить_работу(текущая_нода);
}
 
// Если время вышло — отдаём управление браузеру

2. Связный список вместо стека

Code Example 3: В чём принципиальное отличие между этими двумя подходами к обходу дерева? Почему первый нельзя прервать, а второй можно?

js
// ❌ Stack Reconciler — нельзя прервать
function reconcile(element) {
  processElement(element);
  element.children.forEach(child => {
    reconcile(child); // рекурсия
  });
}

3. Double Buffering (два дерева)

React хранит два Fiber-дерева одновременно:

text
┌─────────────────────────────────────────────────┐
│  Current Tree          Work-in-Progress Tree   │
│  (на экране)           (строится)              │
│                                                 │
│    [App]  ◄──alternate──►  [App]               │
│      │                       │                  │
│   [Header]                [Header]              │
│      │                       │                  │
│  [Content]               [Content'] ← изменён  │
└─────────────────────────────────────────────────┘

После commit деревья меняются местами.

4. Приоритеты обновлений

ПриоритетПримерыОписание
ImmediateКлик, вводДолжен выполниться сейчас
UserBlockingHoverВысокий приоритет
NormalFetch данныхОбычное обновление
LowSuspense fallbackМожно отложить
IdleOffscreenКогда браузер свободен

Как Fiber работает: пошаговый разбор

Шаг 1: Trigger (триггер обновления)

Обновление запускается через:

  • setState() / useState setter
  • ReactDOM.render()
  • forceUpdate()

Шаг 2: Schedule (планирование)

React определяет приоритет и планирует работу:

js
// React внутри себя делает примерно это:
scheduleUpdateOnFiber(fiber, lane, eventTime);

Шаг 3: Render Phase

React обходит дерево и вычисляет изменения:

Code Example 4: Что делает функция workLoopConcurrent? Что означает условие !shouldYield()?

js
// Цикл работы
function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}
Эту фазу МОЖНО прервать.

Шаг 4: Commit Phase

React применяет все изменения к DOM:

js
// Три подфазы:
// 1. Before mutation — читаем текущее состояние DOM
// 2. Mutation — изменяем DOM
// 3. Layout — вызываем useLayoutEffect, componentDidMount
Эту фазу НЕЛЬЗЯ прервать.

Что дал Fiber

Возможности, которые появились благодаря Fiber:

  1. Suspense — приостановка рендера для загрузки данных
  2. Error Boundaries — перехват ошибок в дереве компонентов
  3. Concurrent Mode — параллельная работа над несколькими версиями UI
  4. Transitions — разделение срочных и несрочных обновлений
  5. useTransition / useDeferredValue — хуки для управления приоритетами
⚠️

Fiber появился в React 16, но Concurrent Mode стал стабильным только в React 18.


Сравнение Stack vs Fiber

АспектStack ReconcilerFiber
Прерываемость❌ Нет✅ Да
Приоритеты❌ Нет✅ Да
Обход дереваРекурсия (стек вызовов)Linked list
ПамятьМеньшеБольше (два дерева)
Concurrent features❌ Невозможны✅ Возможны
Простота✅ Проще понять❌ Сложнее

Пограничные кейсы

🚫

Важно: Render phase может выполняться несколько раз! Поэтому render должен быть чистой функцией без side effects.

Code Example 5: Какая проблема в первом варианте? Почему side effect в render фазе опасен в контексте Fiber?

jsx
// ❌ Опасно — side effect в render
function Counter({ count }) {
  document.title = `Count: ${count}`; // может вызваться многократно!
  return <div>{count}</div>;
}
 
// ✅ Безопасно — side effect в useEffect
function Counter({ count }) {
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);
  return <div>{count}</div>;
}

Вопросы интервьюера

Q: Чем Fiber отличается от Virtual DOM?

Virtual DOM — это концепция (представление UI в памяти). Fiber — это конкретная реализация работы с этим представлением в React.

Q: Fiber делает React многопоточным?

Нет, JavaScript остаётся однопоточным. Fiber позволяет "разбивать" работу на кусочки и уступать главному потоку между ними.

Q: Когда появился Fiber?

React 16 (сентябрь 2017). Concurrent Mode стал стабильным в React 18 (март 2022).

Q: Можно ли использовать Fiber напрямую?

Нет, Fiber — внутренняя реализация React. Разработчики взаимодействуют с ним через публичные API (hooks, setState и т.д.).


Источники

Code Example 1: Fiber node structure

❓ Какие основные поля содержит Fiber-нода? Объясните назначение полей child, sibling, return и alternate.

ts
interface Fiber {
  type: any;
  key: string | null;
 
  child: Fiber | null;
  sibling: Fiber | null;
  return: Fiber | null;
 
  pendingProps: any;
  memoizedProps: any;
  memoizedState: any;
 
  flags: number;
 
  alternate: Fiber | null;
}

Code Example 2: Unit of Work

❓ Как Fiber позволяет разбивать работу на части? Что происходит, когда "время вышло"?

js
while (есть_работа && есть_время) {
  текущая_нода = выполнить_работу(текущая_нода);
}

Code Example 3: Stack vs Fiber traversal

❓ В чём принципиальное отличие между этими двумя подходами к обходу дерева? Почему первый нельзя прервать, а второй можно?

Stack Reconciler:

js
function reconcile(element) {
  processElement(element);
  element.children.forEach(child => {
    reconcile(child);
  });
}

Fiber:

js
function performWork(fiber) {
  doWork(fiber);
 
  if (fiber.child) return fiber.child;
  if (fiber.sibling) return fiber.sibling;
  return fiber.return?.sibling;
}

Code Example 4: Work loop

❓ Что делает функция workLoopConcurrent? Что означает условие !shouldYield()?

js
function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

Code Example 5: Side effects in render

❓ Какая проблема в первом варианте? Почему side effect в render фазе опасен в контексте Fiber?

Version A:

jsx
function Counter({ count }) {
  document.title = `Count: ${count}`;
  return <div>{count}</div>;
}

Version B:

jsx
function Counter({ count }) {
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);
  return <div>{count}</div>;
}