Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке React і навіщо він потрібен?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**React** - це JavaScript-бібліотека для побудови інтерфейсів з повторно використовуваних компонентів, кожен з яких керує власним станом і рендерингом. ```jsx function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>Лічильник: {count}</button>; } ``` **Ключове:** React декларативний - ти описуєш, яким має бути UI для певного стану, а React сам оновлює DOM.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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>` і оновлює тільки його.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.