Що таке React і навіщо він потрібен?
React - це JavaScript-бібліотека з відкритим кодом для побудови користувацьких інтерфейсів (UI) з повторно використовуваних компонентів.
Теорія
TL;DR
- React схожий на кухню ресторану: кожен кухар (компонент) готує свою страву незалежно, а шеф-кухар (React) оновлює на тарілці тільки те, що змінилось, не переготовуючи все заново.
- Головна різниця: декларативний підхід (описуєш який UI хочеш) проти імперативного (вручну кажеш DOM що змінювати, крок за кроком).
- Virtual DOM стоїть між твоїм кодом і реальним DOM, збирає зміни в пакети і мінімізує оновлення браузера.
- Використовуй React для додатків з частими змінами стану і динамічним UI. Для статичних сторінок він зайвий.
Швидкий приклад
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // стан запускає повторний рендер
return (
<div>
<p>Лічильник: {count}</p> {/* UI описано як функцію від стану */}
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}Хук useState зберігає count між рендерами. Коли викликається setCount, React порівнює новий virtual DOM зі старим і оновлює тільки текстовий вузол у <p>. Ніяких document.querySelector, ніяких ручних присвоєнь.
Декларативний проти імперативного
Декларативна модель React каже: "коли count дорівнює 5, показуй ось це." Ти описуєш результат, а не кроки. Vanilla JS змушує тебе знайти DOM-вузол, оновити текст і обробити кожен крайній випадок вручну. Це втричі більше коду і втричі більше місць, де може ховатись баг.
Коли в продакшені є 20 компонентів, що читають один і той же стан, різниця стає дуже відчутною.
Virtual DOM: навіщо він потрібен
DOM браузера повільний для частих оновлень. React тримає легковагову JavaScript-копію DOM (virtual DOM). При кожній зміні стану запускається алгоритм порівняння (reconciliation), що шукає різницю між новим і попереднім знімком virtual DOM. До реального DOM записуються тільки вузли, які змінились.
React 18 додав архітектуру Fiber, яка розбиває роботу рендерингу на дрібні одиниці і розподіляє їх по кадрах браузера з пріоритетами. Введення користувача йде першим. Фонові запити даних можуть почекати.
Коли використовувати React
- Динамічні списки і форми з додаванням, видаленням, фільтрацією: React.
- Статична маркетингова сторінка або лендінг: чистий HTML/CSS, React не потрібен.
- Full-stack додаток з серверним рендерингом: Next.js (React з SSR під капотом).
- Мобільні додатки: React Native, той самий JSX-синтаксис, нативний вивід.
- Проста інтерактивність на статичній сторінці: vanilla JS або HTMX буде достатньо.
Типові помилки
Мутація стану напряму:
// Неправильно: React бачить той самий референс масиву, пропускає ре-рендер
const [todos, setTodos] = useState([]);
todos.push(newTodo);
setTodos(todos);
// Правильно: новий референс запускає diff
setTodos([...todos, newTodo]);Індекс масиву як key:
// Неправильно: ламає reconciliation при зміні порядку
{items.map((item, index) => <li key={index}>{item.text}</li>)}
// Правильно: стабільний ID
{items.map(item => <li key={item.id}>{item.text}</li>)}Відсутнє очищення в useEffect:
useEffect(() => {
const id = setInterval(fetchData, 5000);
return () => clearInterval(id); // без цього: витік пам'яті
}, []);Застаре замикання (stale closure) у useEffect:
// count всередині інтервалу завжди 0
useEffect(() => {
const id = setInterval(() => console.log(count), 1000);
return () => clearInterval(id);
}, []); // пусті залежності фіксують count=0 назавжди
// Виправлення: додай count до залежностей або використай refДе зустрічається в реальних проектах
- Netflix: компоненти плеєра, virtual DOM забезпечує плавне перемотування.
- Facebook Newsfeed: React Fiber обробляє оновлення для мільярдів користувачів.
- Airbnb: динамічні фільтри пошуку на хуках.
- Shopify: Next.js (React з SSR) для сторінок e-commerce.
- Instagram: React Native ділить UI-логіку з веб-додатком.
Follow-up питання
Q: Що таке virtual DOM і навіщо він React?
A: Об'єктне дерево JavaScript, що дзеркалить реальний DOM. React порівнює його при кожному оновленні, щоб знайти мінімальний набір змін для реального DOM. Менше записів до DOM означає швидший UI.
Q: Що таке JSX?
A: Синтаксичний цукор, що компілюється в React.createElement('div', null, 'text') через Babel. Це не HTML і не обов'язково, але він робить дерева компонентів набагато читабельнішими.
Q: Яка різниця між useState і useReducer?
A: useState підходить для простих ізольованих значень. useReducer зручний для складної логіки стану, де кілька значень взаємопов'язані або наступний стан залежить від попереднього.
Q: Навіщо потрібні незмінні (immutable) оновлення?
A: React перевіряє рівність стану через ===. Якщо мутуєш об'єкт напряму, референс залишається тим самим, React не бачить змін і пропускає ре-рендер. Незмінні оновлення створюють нові референси, що правильно запускає diff.
Q: Що додає concurrent mode у React 18?
A: useTransition дозволяє позначити нетермінові оновлення, щоб React міг їх призупинити і спочатку обробити введення користувача. UI залишається відзивчивим під час важких запитів даних.
Приклади
Базовий: компонент лічильника
import { useState } from 'react';
import { createRoot } from 'react-dom/client';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Лічильник: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
createRoot(document.getElementById('root')).render(<Counter />);
// Клік на '+1' оновлює тільки текстовий вузол у <p>, не весь DOMСтан живе в компоненті. При виклику setCount React робить ре-рендер Counter, порівнює вивід і патчить єдиний змінений текстовий вузол у реальному DOM.
Середній: список завдань з незмінними оновленнями стану
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Вивчити React', done: false }
]);
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)); // spread створює новий об'єкт - React бачить зміну
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.text} {todo.done ? '(виконано)' : ''}
</li>
))}
</ul>
);
}Клік на завдання запускає toggleTodo. Оператор spread створює новий об'єкт для зміненого елемента і новий масив для todos. Diff React знаходить змінений <li> і оновлює тільки його.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.