Чому фронтенд стає складним після React: стан, ефекти, межі та інструменти ШІ
React ускладнив створення інтерфейсів, але не полегшив їхнє проектування. Там, де знаходиться стан, виникають складні ефекти, проблеми з межами системи та код, створений штучним інтелектом — ось справжня робота над фронтендом.
Джерела: React Isn’t the Hardest Part of Frontend Development
Деякі розробники фронтенду можуть створити компонент React за двадцять хвилин, а потім витратити наступні три години на з’ясування причин, чому його тепер неможливо підтримувати. Вина не в JSX, не в useState, і навіть не в самому React. Справжніми труднощами у роботі з фронтендом є аспекти, пов’язані з фреймворком: розуміння того, як має поводитися продукт, вибір місця зберігання стану, координація асинхронних викликів, визначення меж компонентів, робота з особливостями браузерів, забезпечення доступності та швидкості інтерфейсів, а також створення коду, який залишатиметься зрозумілим навіть через півроку.
React полегшив створення інтерфейсів. Однак він ніколи не робив інженерію фронтенду простішою, і ця галузь занадто довго вдавала, ніби це так.
Що виправив React та що залишив без змін
До появи React складнощі були іншого характеру. Розробники вручну редагували DOM та по одному під’єднували обробники подій, і їм було важко підтримувати відповідність екрана даним програми. У міру зростання додатків ставало все складніше відстежувати взаємозв’язок між діями користувача, даними та відображеними елементами.
React запропонував корисну концепцію: інтерфейс — це функція стану. Замість того, щоб вказувати браузеру на кожну зміну DOM, ви описуєте, як виглядає інтерфейс для певного стану. Компоненти визначають межі, пропси надають чіткі вхідні дані, стан представляє дані, які змінюються з часом, а декларативне відображення усуває багато випадкової складності. Це був справжній крок вперед.
Проте фреймворк вирішує лише ті проблеми, для яких він був створений. React не має думки щодо того, чи потрібен додатку п’ять елементів глобального стану чи п’ятдесят. Він не скаже, чи компонент виконує занадто багато функцій, чи розробка API є логічною, чи індикатори завантаження розташовані на правильних місцях, чи процес оформлення покупки є зрозумілим для покупця. І він точно не може вирішити, що має робити продукт, коли з’єднання переривається під час запиту. Усі це також є проблемами фронтенду.
Звичайна функція стикається з реальністю
Візьмемо звичайну панель керування. Користувач її відкриває, додаток отримує дані, він змінює фільтр, результати оновлюються, він обирає товар, відкривається модальне вікно, він редагує та зберігає зміни, сервер відповідає, і панель керування оновлюється. У всьому цьому немає нічого складного.
Тепер додамо те, що насправді відбувається. Перше запитання зазнає невдачі. Фільтр змінюється, поки попереднє запитання ще у процесі обробки, і старіша відповідь надходить після новішої. Модальне вікно відкривається з глибокого посилання. Сторінка оновлюється під час редагування. Відкрито дві вкладки. Сесія закінчується терміном дії. API повертає частину даних. Сервер приймає зміни, але клієнт так і не бачить відповіді, і користувач двічі натискає кнопку збереження. У списку 10 000 рядків, з’єднання повільне та через мобільний пристрій, інтерфейс має працювати лише за допомогою клавіатури, а повідомлення про помилку має пояснювати, що робити далі.
У цей момент код React є найменш цікавою частиною. Усе це можна створити за допомогою React, і усе це можна зробити погано. Багато розробників не помічають цієї різниці.
Завдання полягає у керуванні складністю
Робота фронтенд-розробника полягає не стільки у написанні компонентів, скільки у усуненні складнощів, щоб користувач бачив просту структуру, що є набагато складніше. Добре створений компонент може містити п’ятдесят рядків коду, але досягнення цього обсягу замість п’ятисот вимагає низки рішень:
- Звідки беруться дані, хто є їхнім власником, як довго вони існують та хто може їх змінити
- Що відбувається, коли запит зазнає невдачі
- Чи має стан зберігатися під час навігації чи оновлення
- Чи він має знаходитися в URL, у локальному стані компонента чи в кеші сервера
- Чи два компоненти повинні ділитися самими даними чи просто обчислювати схожі значення самостійно
React не відповідає на жодне з цих запитань. Розробник це робить, і відповіді накопичуються. Слабке рішення рідко залишається лише в одному компоненті — воно перетворюється на шаблон, інші розробники його копіюють, і незабаром у додатку з’являється послідовна архітектура, яку ніхто не обирав. Саме так кодові бази фронтенду стають складними для керування: рішення накопичуються, а складність React майже не має до цього стосунку.
Стан як проблема моделювання
Швидкий спосіб виявити незрілий дизайн фронтенду — це переглянути його стан. Інтенсивне використання useState саме по собі не є помилкою. Проблема в тому, що стан розглядається як місце для зберігання значень, а не як щось, що потрібно моделювати. Розробник бачить значення, яке змінюється, і починає використовувати відповідний хук.
const [value, setValue] = useState(...)
Здається, що все готово, але справжнє питання не в тому, як зберігати значення. Справжнє питання — чи взагалі має існувати це значення. Подивіться на цю трійку:
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [fullName, setFullName] = useState("");
Зараз три елементи системи зберігають інформацію, яку можна було б отримати з двох. У цьому немає вини React – проблема у ментальній моделі.
Те саме відбувається у масштабі: команди зберігають відповіді API, фільтровані копії цих відповідей, позначки завантаження, обчислені підсумки, вибрані записи, позначки модалних вікон та дублікати однієї entитету в кількох місцях. З часом ніхто не може сказати, яка копія є авторитетною, і з’являються баги – один компонент змінює копію, тоді як інший читає іншу. Ефект синхронізації намагається їх узгодити, а інший ефект реагує на перший. Раптово браузер стає частиною невеликої розподіленої системи. Цю архітектуру не створював React – її створили люди, які пишуть код.
Ефекти розкривають дизайн
Якщо якась функція React зазнає більше критики, ніж інші, то це useEffect, і ця критика часто є обґрунтованою. Проте саме тут зазвичай проявляються архітектурні проблеми, а не там, де вони починаються.
Уявіть компонент, який містить кілька таких ефектів: один отримує дані, один синхронізує стан, один реагує на зміну параметра, один записує дані у локальну пам’ять, один надсилає дані в систему аналітики, а ще один скидає щось при зміні умови. Кожен з них окремо здається розумним. Разом вони утворюють граф залежностей, який важко простежити. Розробник додає залежності, поки інструмент перевірки не припинить сигналізувати про проблеми, або видаляє деякі через надмірну частоту виконання ефектів, або додає булевий реф, щоб уникнути подвійного виконання. Зрештою компонент працює у тестовому сценарії, але ніхто точно не знає чому.
Це не недолік React. Йдеться про нечітке розподілу відповідальності та заплутані побічні ефекти, а React просто надає їм місце для існування.
Браузер — це лише один вузол у більшій системі
Обсяг роботи з фронтендом постійно зростає. Сучасні додатки взаємодіють з API та базами даних, системами автентифікації та аналітикою, а також із функціями типу feature flags, постачальниками платежів та іншими сервісами третіх сторін. До цього додаються функції типу edge functions, серверні компоненти, фонові завдання та, все частіше, сервіси, що ґрунтуються на ШІ. Браузер є лише одним учасником у набагато більшій системі, тому розробник фронтенду повинен розуміти не лише процес відображення контенту, а й те, що відбувається до та після того, як запит покидає сторінку.
Це не означає, що кожен мусить стати спеціалістом з бекенду. Це означає, що стара схема фронтенд/бекенд стає все менш корисною. Той, хто не знає HTTP, зрештою буде мати проблеми з кешуванням. Той, хто не знайомий з базами даних, буде мати труднощі з розумінням принципів послідовності, а той, хто не розуміє механізмів автентифікації, буде стикатися з проблемами під час роботи з сеансами. Умови конкуренції та повторні спроби заплутують розробників без досвіду роботи з розподіленими системами, а ті, хто не розуміє користувачів, створюють елегантні інтерфейси, які нікому не подобаються. Фреймворк — це єдиний шар.
Штучний інтелект змінює економіку, але не складні аспекти
Асистенти з кодування на основі ШІ змінили структуру витрат на роботу з фронтендом. Створення компонентів, форм, перетворення дизайну у JSX, використання хуків та тестів стало дешевшим. Це може здатися загрозою, але краще розглядати це як перевірку того, де насправді полягала цінність. Якщо ваш основний внесок — це швидке набирання коду JSX, то ШІ становить проблему. Якщо ж це розуміння систем, архітектури, поведінки продукту, компромісів та потреб користувачів, то ШІ змінює ваш робочий процес, залишаючи складні завдання без змін, і, ймовірно, підвищуючи їхню важливість.
Асистент може за кілька секунд підготувати три правдоподібні версії функції. Вибір між ними — це архітектурне рішення, а не синтаксичне. Він може створити систему керування станом, але чи має цей стан бути глобальним? Він може написати оптимістичне оновлення, але чи справді додаток має поводитися оптимістично? Він може додати кеш, але якою буде стратегія його анулювання? Він може написати ефект, але чи має цей ефект існувати? Чим швидше з’являється код, тим більше необхідно ретельно обмірковувати рішення, і саме це, ймовірно, визначатиме роботу над фронтендом у найближчі кілька років.
Кваліфіковані розробники фронтенду схильні менше думати про React, а більше — про межі функціоналу. Вони запитують, де полягає відповідальність, та помічають, коли компонент перетворюється на невелике додаток. Вони борються з дублюванням даних, планують сценарії збоїв ще до успішного виконання завдань, знають, що насправді гарантує API, та звертають увагу на неочікувану поведінку користувачів. Вони можуть визначити, коли абстракція допомагає, а коли лише приховує складність. Вони розуміють принципи роботи браузера, налаштовують продуктивність, не оптимізуючи все занадто рано, і, що найважливіше, можуть пояснити, чому система працює саме так. Це набагато складніша навичка для автоматизації та діє ефективніше, ніж знання найновішого API React.
Знання фреймворку з часом стають застарілими
Розробники недостатньо обговорюють це питання: знання фреймворків мають термін придатності. API React, який був вивчений п’ять років тому, вже не є актуальним сьогодні. Найкращі практики змінюються, бібліотеки зникають, інструменти для розробки оновлюються, стратегії відображення еволюціонують, можливості сервера розширюються, а з’являються нові фреймворки. Синтаксис змінюється, проте основні інженерні проблеми залишаються дуже стабільними:
- Моделювання стану та передача даних у системі
- Керування помилками та асинхронною роботою
- Уникнення зайвої взаємозалежності
- Забезпечення прозорості системи та доступності інтерфейсу
- Контроль складності
Ці проблеми пережили jQuery, Angular та Backbone, а також ранню екосистему React. Вони переживуть усе, що буде після них. Тож навчання фронтенду виключно через фреймворки — це крихкий підхід. Вивчайте фреймворк, але не плутайте його з самою дисципліною.
Можливо, одного дня React перестане бути центром уваги. Його може замінити щось інше, кілька підходів можуть існувати паралельно, або він може залишатися домінуючим набагато довше, ніж очікується. Це не має великого значення. Розробники, чия професійна ідентичність пов’язана з API React, будуть змушені їх перебудувати, тоді як ті, хто зрозумів, як насправді працюють інтерфейси, зможуть адаптуватися.
React — це інструмент, а не мистецтво
Розробка фронтенду ніколи не була пов’язана виключно з React. React — це дуже хороший інструмент для створення фронтенд-додатків, і він залишається інструментом. Складною є задача вирішити, що саме потрібно відобразити: як взаємодіють елементи, що відбувається, коли щось ламається, де знаходиться стан, що працює в браузері та на сервері, як система розвивається разом із продуктом та як приховати все це від користувача. Ось у чому полягає робота, а JSX — це лише спосіб оформлення частини цього коду.
Чим краще інструменти створюють код, тим меншою стає його цінність. Однак це не припиняє розробку програмного забезпечення, хоча змінює те, про що мають думати розробники під час роботи. Той, хто може швидко створити компонент, є корисним, а той, хто може вирішити, чи варто він існувати, — ще кориснішим. Те саме стосується інтеграцій API: створення одного є корисним, але виявлення помилок у самому контракті має ще більшу цінність. Розробник, який може попросити ШІ переробити кодову базу, має цінність, а той, хто може визначити, чи покращує запропонована переробка систему, — ще більшу.
Тож ніхто не повинен хвилюватися через те, що React стає простішим у використанні. Саме це і є метою, адже мета фреймворку — скоротити зайву роботу, і досвідчений розробник використовує заощаджений час на вирішення справді важливих проблем.
Пов’язана література
- Створення агента ReAct у LangChain: виконавці, блокноти та їхні обмеження — Створіть агента ReAct, який використовує інструменти, подивіться, як агент та AgentExecutor ділять роботу, та дізнайтеся, чому LangGraph краще справляється з контролем потоку, циклами та станом.
- Коли оптимізація React призводить до проблем: чому менше перерисовувань уповільнило додаток — Як функції memo, useMemo та useCallback додали навантаження та баги у великій сторінці React, та чому переміщення стану та аналіз усіх взаємодій вирішило проблему.