Skip to main content

Що таке React і навіщо він потрібен?

React - це JavaScript-бібліотека з відкритим кодом для побудови користувацьких інтерфейсів (UI) з повторно використовуваних компонентів.

Теорія

TL;DR

  • React схожий на кухню ресторану: кожен кухар (компонент) готує свою страву незалежно, а шеф-кухар (React) оновлює на тарілці тільки те, що змінилось, не переготовуючи все заново.
  • Головна різниця: декларативний підхід (описуєш який UI хочеш) проти імперативного (вручну кажеш DOM що змінювати, крок за кроком).
  • Virtual DOM стоїть між твоїм кодом і реальним DOM, збирає зміни в пакети і мінімізує оновлення браузера.
  • Використовуй React для додатків з частими змінами стану і динамічним UI. Для статичних сторінок він зайвий.

Швидкий приклад

jsx
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 буде достатньо.

Типові помилки

Мутація стану напряму:

jsx
// Неправильно: React бачить той самий референс масиву, пропускає ре-рендер const [todos, setTodos] = useState([]); todos.push(newTodo); setTodos(todos); // Правильно: новий референс запускає diff setTodos([...todos, newTodo]);

Індекс масиву як key:

jsx
// Неправильно: ламає reconciliation при зміні порядку {items.map((item, index) => <li key={index}>{item.text}</li>)} // Правильно: стабільний ID {items.map(item => <li key={item.id}>{item.text}</li>)}

Відсутнє очищення в useEffect:

jsx
useEffect(() => { const id = setInterval(fetchData, 5000); return () => clearInterval(id); // без цього: витік пам'яті }, []);

Застаре замикання (stale closure) у useEffect:

jsx
// 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 залишається відзивчивим під час важких запитів даних.

Приклади

Базовий: компонент лічильника

jsx
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.

Середній: список завдань з незмінними оновленнями стану

jsx
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> і оновлює тільки його.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?