РозробкаБудь-який рівень

React

React — бібліотека JavaScript для побудови інтерфейсів із компонентів, кожен з яких описує, що має бути на екрані за поточного стану. Далі React сам з’ясовує, як змінити сторінку, щоб вона цьому відповідала, і майже всі складнощі з ним походять із порушення цієї моделі: нечиста робота під час рендеру, стан там, де він не може бути джерелом правди, або ефект, що робить чужу роботу. Сторінка починається з усіх вбудованих хуків на одному екрані й чотирьох причин, з яких компонент рендериться; далі — рендер і коміт, перерендери, стан як знімок, ефекти, серверні компоненти й п’ять задокументованих кейсів.

Поточна версія
19.3 · вересень 2026
Модель
UI = f(стан)
Керує
React Foundation · 2026

React на одному екрані: кожен хук і для чого він

Компонент React — це функція, що повертає опис екрана, а хуки — функції, які вона викликає, щоб отримати все інше: стан, значення згори дерева, зв’язок із зовнішнім світом. Тут вони всі одразу, бо разом вони становлять майже все, що ви пишете в компоненті. Таблиця перелічує кожен вбудований хук, який документує react.dev станом на React 19.3 (вересень 2026), за групами — із роботою, для якої його створено, і тим, як його зловживають.

ХукДля чого вінЯк його зловживають
useStateЗначення, що належить цьому компоненту й змінюється з часомЗберігати те, що можна обчислити з іншого стану чи пропсів
useReducerСтан із кількох частин, що змінюються разом, і одна чиста функція оновленняСховище всього застосунку, відтворене вручну в одному компоненті
useContextЧитати значення, надане вище, не передаючи його через кожен рівеньЗначення, що змінюється багато разів на секунду: щоразу перерендерюється кожен читач
useRefЗначення, що переживає рендери, не спричиняючи їх, — вузол DOM, id таймераЧитати чи писати його під час рендеру, де React не бачить змін
useImperativeHandleОбрати, що батько може викликати через ref на ваш компонентІмперативний API там, де вистачило б звичайного пропа
useEffectТримати компонент у згоді із системою поза React — після оновлення екранаОбчислювати значення чи реагувати на клік — розділ про ефекти нижче
useLayoutEffectВиміряти сторінку до того, як браузер намалює кадр, — позицію підказкиБудь-що, що нічого не вимірює: він затримує відмальовку
useInsertionEffectБібліотеки CSS-in-JS, що вставляють стилі до зміни DOMКод застосунку — він не для вас
useEffectEventЧитати найсвіжіші пропси всередині ефекту, не змушуючи його перезапускатися (19.2)Загортати в нього все підряд, щоб заглушити лінтер залежностей
useMemoЗберігати дорогий результат між рендерамиЗагортати дешеву роботу, де порівняння коштує більше за саму роботу
useCallbackТа сама функція між рендерами — для мемоїзованої дитини чи ефектуКожна функція за замовчуванням, як ритуал
useTransitionПозначити оновлення як нетермінове, щоб набір тексту лишався чуйнимЧекати, що він зробить повільний код швидким, — він лише переставляє роботу
useDeferredValueДати повільній частині екрана відставати від швидкого введенняЗатримка мережевих запитів — він відкладає рендер, а не завантаження
useActionStateРезультат дії форми й те, чи вона ще виконуєтьсяУніверсальний редʼюсер для стану, що не має стосунку до дії
useOptimisticПоказувати очікуваний результат, поки дія ще в дорозіЗабути, що він відкочується, коли дія падає, — невдача потребує повідомлення
useПрочитати проміс чи контекст — єдиний, який дозволено всередині умовиПроміс, створений під час рендеру: щоразу новий, тож компонент раз у раз призупиняється
useFormStatusЧи надсилається форма, всередині якої стоїть цей компонент (react-dom)Викликати його в компоненті, що сам рендерить <form>, — він бачить лише форму над собою
useIdСтабільний id, що пов’язує лейбл із полем, однаковий на сервері й клієнтіКлючі для списку — ключі беруться з даних
useSyncExternalStoreПідписатися на сховище, яким React не володіє, — сховище бібліотеки, браузерний APIОбгортати стан, який міг бути просто useState
useDebugValueПідпис для вашого власного хука в React DevToolsДороге форматування на кожному рендері — передайте функцію-форматер

Описуйте, а не наказуйте: рендер і коміт

До React фронтенд-код здебільшого давав інструкції: знайди цей вузол, задай йому текст, додай клас, прибери той рядок. Кожна інструкція потребувала парної, що її скасовує, і кількість пар росла швидше за кількість фіч — тому старі фронтенд-бази й ламалися в місцях, яких ніхто не чіпав. React це перевертає. Ви пишете функцію, яка за поточним станом повертає опис інтерфейсу, а React з’ясовує, які операції над потрібні, щоб його досягти. Ви ніколи не прибираєте рядок; ви описуєте список, у якому його вже немає. Правильним має бути один опис, а не набір переходів між станами, що лише росте.

Зміна стану змушує React знову викликати ваші компоненти — це фаза рендеру, і вона чиста: вона створює опис інтерфейсу, і її можна перервати, відкинути або повторити. Лише фаза коміту торкається DOM, і вона стається один раз. Ефекти виконуються після коміту. Саме тому побічна дія, написана під час рендеру, поводиться дивно, а той самий код усередині ефекту — ні.

Схема ділить роботу на дві фази, і цей поділ — найнесучіша ідея цієї сторінки. Фаза рендеру викликає ваші компоненти, щоб отримати опис. Вона має бути чистою — ті самі входи дають той самий результат, і ніщо поза функцією не змінюється, — бо React може виконати її двічі, призупинити на півдорозі або викинути результат. Фаза коміту застосовує різницю до справжнього DOM, один раз. Ефекти виконуються після коміту, коли сторінка вже актуальна.

Коли компонент перерендерюється

Чотири причини з верху сторінки на практиці дають одну картину. Коли в компоненті змінюється стан, рендериться цей компонент і все під ним, аж до самого низу, — хай діти отримують щось змінене чи ні. Нічого над ним не рендериться. Це свідоме налаштування за замовчуванням: викликати функцію й порівняти результат дешево, а перерендер — це не оновлення DOM. Дорогим він стає лише тоді, коли піддерево велике або якийсь компонент у ньому під час рендеру робить важку роботу.

Коли стан змінюється в Page, рендериться Page і все, що він рендерить, аж до листків, — зокрема Filters, чиї пропси не змінилися. Нічого над Page не рендериться. Каскад зупиняють дві речі: List загорнутий у memo й отримує рівні пропси, тож React пропускає його разом із рядками; Sidebar створив App і передав як children, тож це той самий елемент, що й минулого разу, і React теж його пропускає. Дитина рендериться тому, що відрендерився батько, а не тому, що змінилися її пропси.

Діаграма показує три виходи, і їх варто пробувати в такому порядку, бо перші два безкоштовні, а третій має ціну. Перший — опустіть стан нижче: якщо значення цікавить лише одне невелике піддерево, тримайте стан там, і решта сторінки перестане рендеритися. Другий — передавайте шматки екрана через children: елемент, створений батьком, який не перерендерювався, — той самий елемент, що й минулого разу, і React його пропускає. Лише тоді беріть memo, useMemo і useCallback. Кожен додає порівняння до кожного рендеру, і кожен окупається лише тоді, коли пропущена робота справді більша за це порівняння.

З жовтня 2025 року існує четвертий варіант, що змінює арифметику. React Compiler 1.0 — це крок збірки, який читає ваші компоненти й сам вставляє мемоїзацію, точніше, ніж її пишуть руками, — він уміє мемоїзувати після раннього return, чого не може написаний вручну useMemo. У Meta він пришвидшив перше завантаження й переходи в Quest Store до 12%, а деякі взаємодії — більш ніж у два з половиною рази. Умова одна: ваш код має дотримуватися правил React, і передусім чистоти рендеру. Компонент, що їх порушує, компілятор не лагодить — він його пропускає, а його правила лінтера кажуть, де саме.

Ключі — це тотожність, а не прикраса

Усередині теми продуктивності ховається одне правило коректності. Рендерячи список, React за key кожного пункту вирішує, який елемент цього рендеру — той самий, що й котрий елемент минулого, а стан, фокус і напівнабраний у полі текст належать саме цій тотожності. Використати індекс масиву як ключ — означає сказати React, що «пункт на позиції два — той самий пункт, що й раніше», а це перестає бути правдою тієї миті, коли список відсортували або прибрали пункт із середини. Видимий результат — текстове поле чи прапорець, що лишається на місці, поки дані рухаються під ним. Беріть стабільний id з даних.

Стан: знімок, черга і одне джерело правди

Усередині одного рендеру стан не змінюється. count — константа на весь цей виклик вашої функції, і виклик setCount її не чіпає: він ставить оновлення в чергу й просить React про ще один рендер. Ось чому три виклики setCount(count + 1) за один клік лишають лічильник на одиниці, а не на трьох: кожен прочитав той самий count, що дорівнював нулю, і кожен попросив одиницю. Якщо передати функцію, setCount(c => c + 1), у черзі стануть три кроки, і кожен отримає результат попереднього.

Усередині одного рендеру стан — це знімок: count дорівнює 0 на весь виклик компонента. setState його не змінює; він ставить оновлення в чергу до наступного рендеру. Три виклики setCount(count + 1) читають той самий 0 і кожен просить 1, тож наступний рендер показує 1. Три виклики setCount(c => c + 1) ставлять у чергу функції, і React передає кожній результат попередньої, тож наступний рендер показує 3.

З React 18 черга ще й групується всюди: усі оновлення, зроблені під час однієї події, — в обробнику, у таймауті чи після await — застосовуються одним рендером, а не кожне своїм. І значення з черги спричиняє рендер, лише якщо React вважає його іншим, а перевіряє він це через Object.is: те саме посилання вважається незміненим. Тож push у масив, що вже лежить у стані, чи зміна поля об’єкта, що вже лежить у стані, дає React ту саму адресу, і на екрані нічого не відбувається. Сторінка про JavaScript малює, що насправді копіює копія; для стану створюйте новий об’єкт для кожного рівня, який змінюєте.

Друга половина проєктування стану — що взагалі заслуговує бути станом. Усе, що можна обчислити з наявного стану чи пропсів під час рендеру, треба обчислювати, а не зберігати. fullName, що лежить у стані поряд із firstName і lastName, — це третє значення, здатне розійтися з двома іншими, і рано чи пізно воно розійдеться. Перевірка пряма: якщо два шматки стану можуть суперечити один одному, один із них похідний і має бути видалений. Команда чату Facebook вивчила це на власному досвіді й розповіла про нього 2014 року: лічильник непрочитаних показував повідомлення, яких ніхто не надсилав, — перший кейс нижче.

Вид стануПрикладДе йому місце
Локальний стан інтерфейсуВідкрите меню, обрана вкладкаuseState у найнижчому компоненті, якому він потрібен
Похідне значенняВідфільтрований список, сума, повне ім’яНіде — обчислюйте під час рендеру
Спільний стан інтерфейсуТема, користувач, що увійшовПіднятий до найближчого спільного батька або в контекст, якщо змінюється рідко
Дані з сервераСписок замовлень, сторінка товаруБібліотека даних чи завантажувач фреймворку — це кеш, а не ваш стан
Стан в адресіПошуковий запит, номер сторінки, фільтрАдреса, через роутер, — щоб він переживав перезавантаження й поширене посилання

Ефекти синхронізують із зовнішнім світом

useEffect існує, щоб тримати компонент у згоді з чимось поза React: підпискою, браузерним , бібліотекою графіків, що володіє власним вузлом DOM, таймером. Це вся його робота. Власна документація React має сторінку «Можливо, ефект вам не потрібен», і це чимало говорить про те, як часто до нього тягнуться за замовчуванням. Більшості ефектів у типовій кодовій базі місце в одному з варіантів праворуч у цій таблиці.

Ви написали ефект, щоб…Зробіть натомість
Обчислити значення при зміні пропсівОбчислюйте під час рендеру. Якщо це справді дорого — useMemo, але спершу виміряйте.
Скинути стан при зміні пропаДайте компоненту key. Новий ключ — це новий компонент зі свіжим станом, і коду не треба.
Надіслати запит при кліку на кнопкуРобіть це в обробнику події. Причина — клік, а не рендер.
Повідомити батька про зміну стануВикличте колбек там, де стається зміна, або підніміть стан, щоб не було про що повідомляти.
Підписатися на сховище чи браузерний APIuseSyncExternalStore, який сам подбає про підписку, відписку й узгодженість.
Завантажити дані, коли відкривається екранЦе доречно, але краще бібліотека даних чи завантажувач фреймворку: у голого ефекту немає кешу й скасування, і він дає перегони.

Коли ефект доречний, дві його частини потребують уваги. Перша — масив залежностей, список значень, зміна будь-якого з яких перезапускає ефект. React порівнює кожен елемент через Object.is, тож об’єкт чи функція, створені під час рендеру, щоразу є новим значенням, і ефект, що від них залежить, виконується після кожного рендеру. На екрані, що рендериться часто, ефект, який викликає API, стає циклом, що викликає його безупинно. Саме це поклало дашборд Cloudflare у вересні 2025 року. Залежте від примітивних значень — id, а не об’єкта, що його містить, — або тримайте об’єкт стабільним через useMemo.

Друга — функція очищення, і вона не є необов’язковою. Вона виконується перед повторним запуском ефекту й тоді, коли компонент зникає, і саме в ній ви відписуєтеся, чистите таймер і скасовуєте запит. Ефект, що підписується без відписки, лишає по слухачу на кожному запуску — у розробці цього не видно, і саме через це вкладка повільна за двадцять хвилин. Ефект, що завантажує дані без очищення, має стан перегонів, якого ваша машина розробника майже ніколи не показує:

Поле пошуку завантажує результати в ефекті щоразу, коли змінюється запит. Користувач вводить «ky», а за 150 мс — «kyi». Перший запит повільніший і відповідає останнім. Без очищення показується та відповідь, що прийшла останньою, тож застарілі результати для «ky» затирають правильні для «kyi» на 700 мс. З очищенням ефект для «ky» прибирається тієї миті, коли змінюється запит, і позначає свій запит застарілим або скасовує його, тож його пізню відповідь відкинуто. Час ілюстративний.

Де React виконується тепер: фреймворки й серверні компоненти

Більшу частину свого життя React був тим, що ви налаштовували самі, а Create React App був стандартним способом почати. У лютому 2025 року команда React відправила його на пенсію й почала радити фреймворки — Next.js, React Router, Expo для мобільних — або простий інструмент збірки на кшталт для тих, хто хоче скласти решту сам. React досі бібліотека, і це реальний компроміс, а не гасло: маршрутизація, завантаження даних, форми й збірка — це рішення, які ви ухвалюєте й потім несете, і дві React-бази в одній компанії можуть не мати майже жодної спільної конвенції. Фреймворк ухвалює ці рішення за вас і приносить із собою ще одне: частина ваших компонентів тепер виконується на сервері.

Серверний компонентКлієнтський компонентСерверна функція
ПозначкаНічого — типовий вибір у фреймворку'use client' на початку файлу'use server'
ВиконуєтьсяНа сервері, до надсилання сторінкиНа сервері для першого HTML, потім у браузеріНа сервері, коли браузер її викликає
МожеЧитати базу чи файл напрямуМати стан, виконувати ефекти, обробляти клікиЗмінювати дані на сервері з форми чи кнопки
Надсилає код у браузерЖодногоУвесьЖодного — але це публічна точка входу

Виграш від серверних компонентів — здебільшого аргумент про розмір бандла. Бібліотека форматування дат, що використовується лише на сервері, не коштує користувачеві нічого, а завантаження даних переїжджає впритул до компонента, якому вони потрібні, без окремого API посередині. Межа, яку треба тримати: усе інтерактивне — onClick, стан, ефект — живе в клієнтському компоненті, тож корисна форма — серверне дерево з малими клієнтськими листками. Саме такої форми збудована ця енциклопедія: кожну статтю, яку ви тут читаєте, рендерять серверні компоненти, а клієнтськими є лише малі листки — меню, пошук, зміст і заголовки таблиць, що лишаються на місці під час прокрутки.

Виконання на сервері має й другу ціну: тепер на сервері працює код React, що перетворює запит назад на дані, а в цьому коді, як і в будь-якому, бувають баги. У грудні 2025 року в ньому знайшли баг з оцінкою 10 із 10 — React2Shell, п’ятий кейс нижче. А ще є шов, де дві половини сходяться. HTML, який надсилає сервер, має збігатися з тим, що відрендерить браузер, коли React перебере сторінку, — цей крок зветься гідратацією; усе, що відрізняється між ними, — поточний час, випадкове число, часовий пояс відвідувача, — дає попередження про розбіжність або мерехтіння. Ставте такі значення в ефект або, починаючи з 19.3, за browser(), щоб сервер їх не вгадував.

П’ять кейсів і те, чого в кожному бракувало

Кожен кейс нижче публічно описала компанія, з якою він стався, або команда React. Три з них — про модель, яку описує ця сторінка, і два — про те, що її оточує: ліцензію й сервер.

КейсЩо сталосяЧого бракувало
Чат Facebook, 2014Лічильник непрочитаних показував повідомлення, яких не існувало, і повертався після кожного виправленняОдне джерело правди й дані, що течуть в один бік
BSD+Patents, 2017Apache заборонив ліцензію, WordPress збирався піти; за кілька тижнів React перейшов на MITЛіцензія, яку могли прийняти юристи користувачів
Create React App, 2025Стандартний стартер для більшості React-застосунків відправили на пенсію, коли супровідників не лишилосяВласник налаштування, яке всі копіювали
Cloudflare, вер. 2025Ефект дашборду перезапускався на кожному рендері й перевантажив сервіс, що авторизує кожен виклик APIСтабільна залежність і повтори з випадковою затримкою
React2Shell, груд. 2025Вада в тому, як серверні компоненти читають запити, дозволяла будь-кому виконати код на серверіЗнання, де React виконується на сервері, і шлях до патча, що міряється годинами

Перший кейс — звідки взагалі походить спосіб, яким React думає про стан. Чат Facebook показував кількість непрочитаних повідомлень у кількох місцях сторінки, і кожне з цих подань тримало й оновлювало власне уявлення про те, що не прочитано. Лічильник раз у раз показував повідомлення, яких не існувало. Інженери його лагодили, і він повертався з наступною фічею, що чіпала чат, бо будь-яке подання могло оновити будь-яке інше, і ніхто не міг утримати в голові всі шляхи. Джинг Чен показала відповідь на F8 у 2014 році: дані течуть в одному напрямку, від єдиного сховища до кожного подання, а подання змінює дані лише тим, що надсилає дію назад на початок. Це Flux, і це те саме правило, що й «якщо два шматки стану можуть суперечити один одному, один із них похідний».

Другий нагадує, що бібліотека — це ще й юридична угода. React публікували під ліцензією BSD із додатковим пунктом про патенти: дозвіл припинявся для кожного, хто подавав на Facebook патентний позов. У липні 2017 року Apache Software Foundation внесла цю ліцензію до списку тих, від яких її проєкти не можуть залежати, а у вересні Метт Малленвеґ оголосив, що WordPress переведе свій новий редактор із React. 22 вересня Facebook переліцензував React під MIT, і за чотири дні під нею вийшов React 16. Код не змінився зовсім. Бракувало ліцензії, яку могли б затвердити юристи компаній, що ним користувалися.

Де він вироджується

Кодова база на React рідко ламається так, щоб це зловив тест. Вона стає повільнішою, на мить показує застаріле значення, рендериться двічі з причин, яких ніхто не може назвати, а її ефекти поволі перетворюються на другу програму, що працює поряд із першою. Ось форми, яких це набуває.

  • Похідні значення, збережені у стані, здатні розійтися з тим, з чого їх вивели.
  • Ефекти для синхронізації двох шматків стану — обчислення, зроблене на кадр пізніше.
  • Об’єкт чи функція в масиві залежностей — і ефект виконується після кожного рендеру.
  • Завантаження в голому ефекті без очищення дає перегони й витоки.
  • Індекс масиву як ключ чіпляє стан і фокус не до того рядка після переупорядкування.
  • Мемоїзація всього за замовчуванням додає порівняння до кожного рендеру й ховає справжню ціну.
  • Зміна стану на місці: React бачить те саме посилання й пропускає оновлення.
  • Усе в одному глобальному сховищі перетворює кожну зміну на подію рівня застосунку.
  • Серверні функції, написані так, ніби їх можуть викликати лише ваші власні кнопки.

Є й чесна межа, і вона вбудована в однорядкову модель нагорі сторінки. React гарантує, що екран відповідає стану. Він нічого не каже про те, чи правильний стан, чи відповіла мережа і чи можна користуватися тим, що на екрані. <div onClick>, відрендерений найкраще впорядкованим деревом компонентів у світі, однаково невидимий для клавіатури й зчитувача екрана, — про це з іншого боку розповідає сторінка про HTML і CSS. І бібліотека не може зробити сторінку легкою: кожен клієнтський компонент — це JavaScript, який телефон мусить завантажити, розібрати й виконати, перш ніж запрацює перший клік.

Практична перевірка та сама, що працює для будь-якого коду інтерфейсу. Відкрийте профайлер у React DevTools, увімкніть підсвічування оновлень і зробіть ту одну дію, заради якої існує екран: кожен спалах не там, де ви торкнулися, — це перерендер, який можна назвати за чотирма причинами. Потім повторіть на телефоні середнього класу. Перша перевірка знаходить змарновану роботу. Друга — чи хтось її відчуває.

Коли це застосовувати

Застосовуйте, коли

  • Інтерфейс має справжній стан, що змінюється в часі та в кількох місцях одночасно.
  • Вам потрібна модель компонентів, і ви візьмете фреймворк або самі оберете маршрутизацію, дані й збірку.
  • Важливий ринок праці — він тут найбільший на фронтенді.
  • Серверні компоненти можуть зняти з телефона завантаження даних і важкі бібліотеки.

Уникайте, коли

  • Контентна сторінка з дрібкою інтерактивності — HTML із невеликим скриптом менший і швидший.
  • Команда хоче отримати конвенції, а не обирати їх, — саме це натомість дає Angular.
  • Серверні компоненти в команді, яка не вміє оновити серверну залежність того ж дня.
  • Додавати його заради одного віджета на серверній сторінці, де рантайм важчий за сам віджет.

Джерела й метод

На чому стоїть ця стаття

  • 12 джерел
  • 2 задокументовані збої
  • Перевірено 13 жовтня 2026 р.

Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.