Головна / Статті / Керування станами користувацького інтерфейсу у реальному світі за допомогою умовного відображення в React

Керування станами користувацького інтерфейсу у реальному світі за допомогою умовного відображення в React

Дізнайтеся, як створювати інтерфейси автентифікації, ролей, дозволів, завантаження, помилок та стану „порожньо“ у React за допомогою практичних шаблонів умовного відображення.

2186 слів

У попередній частині ви розглянули основи умовного відображення — використання простих умов для того, щоб сказати React, чи показувати один компонент, інший компонент чи нічого зовсім.

Однак реальні застосунки рідко залишаються настільки простими:

isLoggedIn ? <Dashboard /> : <Login />

Розгляньмо реальний панель керування. Користувач може перебувати в будь-якому з наступних станів:

  • Виходження з облікового запису
  • Процес входу в обліковий запис
  • Увійшов у систему
  • Адміністратор
  • Звичайний користувач
  • Відсутнє необхідне дозволення
  • Очікування надходження даних
  • Вирішення проблеми з API
  • Перегляд порожнього набору результатів

Кожен з цих сценаріїв вимагає окремого інтерфейсу. Саме тут умовне відображення перестає бути простим трюком та стає справжнім інструментом для структурування вашого застосунку.

Давайте розглянемо, як це виглядає у коді React промислового рівня.

1. Рендеринг на основі автентифікації

Класичним прикладом є автентифікація. Уявіть додаток із двома можливими екранами:

Not Logged In
      ↓
Login Page

Logged In
      ↓
Dashboard

React може легко вибрати між ними:

function App() {
  const isLoggedIn = true;
return (
    <>
      {isLoggedIn ? <Dashboard /> : <Login />}
    </>
  );
}

Однак у реальних додатках зазвичай потрібен ще один стан — loading, адже додатку може знадобитися час, щоб перевірити, чи є токен користувача дійсним. Тоді логіка виглядає так:

Checking Authentication
        ↓
      Loading
        ↓
  Authenticated?
   ↙          ↘
 YES          NO
 ↓             ↓
Dashboard     Login

У коді це може виглядати так:

function App() {
  const isLoading = false;
  const isLoggedIn = true;
if (isLoading) {
    return <LoadingSpinner />;
  }
  return isLoggedIn
    ? <Dashboard />
    : <Login />;
}

Ви побачите, що ця сама схема використовується у безлічі продакшн-додатків на React.

2. Рендеринг на основі ролей

Автентифікація повідомляє вам, хто увійшов у систему. Авторизація повідомляє вам, що ця особа може робити.

Візьмемо за приклад інструмент для керування співробітниками. Адміністратор може бачити:

View Employees
Add Employee
Edit Employee
Delete Employee

Тоді як звичайний користувач бачить лише:

View Employees

Ви можете умовно відображати дії залежно від ролі користувача:

function EmployeeCard({ userRole }) {
  return (
    <div>
      <h2>Employee Details</h2>
      <button>View</button>
      {userRole === "admin" && (
        <>
          <button>Edit</button>
          <button>Delete</button>
        </>
      )}
    </div>
  );
}

Завдяки такій налаштовці елементи керування, доступні лише адміністраторам, з’являються лише для них.

Важлива примітка щодо безпеки

Умовне відображення визначає те, що буде показано у інтерфейсі, але приховування елемента від перегляду не є заміною справжньої безпеки. Наприклад:

{isAdmin && <DeleteButton />}

Це не дозволяє звичайному користувачеві бачити кнопку, але сервер все одно має окремо підтвердити, що запит дійсно надійшов від особи, уповноваженої видаляти дані. Уявіть це так:

Frontend
↓
Controls what users SEE

Backend
↓
Controls what users CAN DO

Ніколи не використовуйте умовне відображення на стороні клієнта як механізм авторизації.

3. Інтерфейс, заснований на дозволах

Більші за обсягом додатки часто потребують більшої деталізації, ніж прості ролі:

Admin
User

Натомість ви можете визначити конкретні дозволи, такі як:

CAN_VIEW_USERS
CAN_EDIT_USERS
CAN_DELETE_USERS
CAN_EXPORT_REPORT

Компоненти можуть потім індивідуально відображати кожну дію залежно від наявних дозволів:

function UserActions({ permissions }) {
  return (
    <>
      {permissions.includes("CAN_EDIT_USERS") && (
        <button>Edit</button>
      )}
      {permissions.includes("CAN_DELETE_USERS") && (
        <button>Delete</button>
      )}
    </>
  );
}

Цей підхід дає значно точніший контроль над тим, що може робити кожен користувач.

4. Стани завантаження

Припустимо, панель керування відправляє запит до API, на обробку якого потрібно два секунди. Що має з’явитися на екрані протягом цього часу? Звісно, не порожня сторінка — потрібен індикатор завантаження:

if (loading) {
  return <p>Loading products...</p>;
}

Загальний алгоритм виглядає так:

API Request
    ↓
Loading = true
    ↓
Show Loader
    ↓
API Response
    ↓
Loading = false
    ↓
Show Content

Індикатори завантаження роблять додаток відповідальним навіть тоді, коли мережа повільна.

5. Індикатори-скелети

Замість простого текстового повідомлення на кшталт:

Loading...

багато сучасних інтерфейсів використовують замінник у формі контенту, який ось-ось завантажиться — індикатор-скелет. Наприклад:

┌──────────────────────┐
│ █████████████        │
│ ███████              │
│ █████████████████    │
└──────────────────────┘

Як тільки справжні дані повертаються, вони замінюють місцевий замінник:

┌──────────────────────┐
│ MacBook Air          │
│ ₹99,999              │
│ ⭐⭐⭐⭐⭐             │
└──────────────────────┘

Основна логіка React залишається такою ж простою:

return loading
  ? <ProductSkeleton />
  : <ProductCard />;

Єдина справжня різниця — це покращення користувацького досвіду, яке вона забезпечує.

6. Стани помилок

Запити до API не завжди працюють без проблем.

З’єднання може втратитися.

Сервер може зламатися.

Запит може закінчитися тайм-аутом.

Замість того, щоб дозволити вашому додатку зламатися або завмерти, слід відображати стан помилки.

if (error) {
  return (
    <div>
      <h2>Something went wrong.</h2>
      <button>Try Again</button>
    </div>
  );
}

Гідне оброблення невдач є ознакою UI високої якості.

7. Завантаження + Помилка + Успіх

На практиці ці три стани майже завжди з’являються разом.

function ProductList({
  loading,
  error,
  products
}) {
      if (loading) {
    return <p>Loading...</p>;
      }
  if (error) {
    return <p>Something went wrong.</p>;
      }
  return <Products products={products} />;
     }

Ви можете уявити цей процес ось так:

Request
   │
   ├── Loading → Loader
   │
   ├── Failed → Error
   │
   └── Success → Data

Коли ви дійдете до API-запитів та useEffect у наступних частинах цього курсу, ви будете бачити цю саму структуру знову та знову.

8. Стани порожнечі

Те, що запит виконався успішно, не гарантує наявності даних для відображення.

Припустимо, користувач шукає щось на кшталт:

"React Quantum Pizza Developer"

Сам API-запит завершується без жодних помилок.

Але результат може виглядати так:

products.length === 0

Замість того, щоб залишати екран порожнім, покажіть користувачеві щось змістовне.

if (products.length === 0) {
  return (
    <div>
      <h2>No Products Found</h2>
      <p>Try changing your search.</p>
    </div>
  );
}

Стани порожнечі мають велике значення для якісного користувацького досвіду.

Завантаження, порожнеча та помилки

Нові розробники React часто плутають ці три стани, але вони означають зовсім різні ситуації.

LOADING
Data hasn't arrived yet.

EMPTY
Data arrived, but nothing exists.

ERROR
Something failed.

Якісний додаток окремо враховує всі три стани.

9. Кілька умов

Іноді те, що ви отримуєте у результаті, залежить від кількох умов, які поєднуються між собою.

Розгляньмо такий алгоритм:

Is User Logged In?
        ↓
Is Subscription Active?
        ↓
Is User Admin?
        ↓
Show Admin Dashboard

Хочеться об’єднати все це в один величезний вкладений тернарний оператор:

condition1
  ? condition2
    ? condition3
      ? <A />
      : <B />
    : <C />
  : <D />

Він гарно компілюється.

Але його дуже складно прочитати.

Кращим підходом є чітке розділення кожної умови.

if (!isLoggedIn) {
  return <Login />;
}

if (!hasSubscription) {
  return <UpgradePlan />;
}

if (isAdmin) {
  return <AdminDashboard />;
}

return <UserDashboard />;

Ця версія набагато зрозуміліша.

10. Умовні оператори захисту

Цей шаблон має назву: умовні оператори захисту, також відомі як передчасні повернення.

Замість того, щоб постійно вкладати умови одну всередину іншої:

if
 └── if
      └── if
           └── UI

спочатку розгляньте крайні випадки та поверніться раніше.

if (loading) return <Loader />;
if (error) return <ErrorPage />;
if (!user) return <Login />;
return <Dashboard />;

Результат є чистим.

Він зрозумілий для читання.

Крім того, його набагато легше дебогувати.

Мисліть як розробник React

Перш ніж писати компонент, корисно поставити собі таке запитання:

„Які всі можливі стани, у яких може перебувати цей екран?“

Для сторінки, яка формується за допомогою виклику API, цей список може виглядати так:

Loading
Error
Empty
Success

У процесі автентифікації він може виглядати так:

Logged Out
Checking Authentication
Logged In
Unauthorized

Попереднє визначення цих станів перед початком програмування значно полегшує розуміння отриманого компонента.

Поширені помилки початківців

Накопичення вкладених умовних операторів

Не жертвуйте читабельністю лише заради економії кількох рядків коду.

Пропуск стану порожнечі

API, яке повертає порожній масив, — це не те саме, що помилка; треба розглядати це як окремий випадок.

Плутання приховування UI з справжньою безпекою

Приховування чогось на кшталт:

<DeleteButton />

нічим не заважає комусь безпосередньо звернутися до вашого кінцевого пункту API.

Справжня авторизація має знаходитися на бекенді.

Дозвіл && відображати неправильний контент

Увага на код на кшталт:

{items.length && <ProductList />}

Якщо items.length дорівнює 0, React може відобразити:

0

саме там на сторінці.

Безпечніша версія — це:

{items.length > 0 && <ProductList />}

Тепер умова оцінюється як справжній булевий значення.

Найкращі практики

У умовному відображенні завжди слід ставити на перше місце читабельність.

Віддавайте перевагу таким патернам:

if (loading) return <Loader />;

замість накопичення умов у глибоко вкладених JSX-елементах.

Якщо ви часто відображаєте певні стани, перенесіть їх у власні компоненти, які можна буде повторно використовувати:

<Loader />
<ErrorMessage />
<EmptyState />

Чим складнішими стають компоненти, тим більше варто розділяти бізнес-логіку від того, що фактично відображається.

І не просто проектуйте лише для оптимістичного сценарію — плануйте на кожен можливий стан інтерфейсу.

Міні-проєкт: Розумна панель керування

Як вправа, спробуйте створити панель керування, яка враховує такі ситуації:

User Not Logged In
        ↓
Login ScreenUser

    Logged In
        ↓
Loading Dashboard
        ↓
 ┌──────┴──────┐
Error          Success
 ↓                ↓
Error UI       Data Exists?
               ↙       ↘
             YES        NO
              ↓          ↓
          Dashboard   Empty State

Потім додайте поведінку, залежну від ролі користувача:

Admin
↓
Edit + Delete

User
↓
View Only

Такий проєкт одночасно поєднує кілька концепцій:

  • Propи
  • Стан
  • Події

Саме так окремі елементи React починають працювати разом, коли ви створюєте щось реальне.

Запитання під час співбесіди

Що таке умовне відображення?

Це практика відображення різного інтерфейсу залежно від поточного стану чи умов у вашому додатку.

У чому різниця між && та тернарним оператором?

Використовуйте &&, коли хочете, щоб щось відображалося лише у правдивому стані, а в інших випадках нічого не показувалося. Використовуйте тернарний оператор, коли потрібен різний вихід у правдивому та хибному станах.

Що вважається порожнім станом?

Це інтерфейс, який ви показуєте, коли запит успішний, але просто немає даних для відображення.

Чи достатньо рендерингу на основі ролей у фронтенді для забезпечення безпеки?

Ні. Це лише зручність інтерфейсу — фактичні перевірки авторизації все одно мають виконуватися на бекенді.

Яка перевага раннього повернення?

Вони зменшують кількість вкладень, що робить умовні компоненти простішими для читання та підтримки.

Ключові висновки

На цьому етапі ви охопили всю сферу умовного рендерингу в React.

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

Умовне відображення — це те, що дозволяє вашому додатку показувати правильний досвід для будь-якої ситуації, в якій він зараз знаходиться.

Але попереду все ще є виклик.

Припустимо, API повертає:

1,000 Products

Чи справді ви б написали:

<Product />
<Product />
<Product />
...

тисячу разів окремо?

Очевидно, що ні.

У React є набагато кращий спосіб вирішення цієї проблеми.

У Частині 9A ви дізнаєтесь, як відображати списки за допомогою map(), та побачите, як один компонент може створювати сотні чи тисячі елементів інтерфейсу безпосередньо з ваших даних.

Відразу після цього ви розглянете одне з найкласичніших запитань під час співбесід у React:

Чому React вимагає наявності key?

Бачимося у Частині 9A — Відображення списків та ключів у React.

Пов’язана література

  • React 19.2 Explained: Activity, useEffectEvent, and Static Rendering — Дізнайтеся, як новий компонент Activity, хук useEffectEvent та часткове статичне відображення у React 19.2 усувають приховані витрати на продуктивність у сучасних інтерфейсах.
  • Створення ментальної моделі для React: узгодження, стан та хуки — Дізнайтеся про логіку основних концепцій React — узгодження, компоненти, пропси, стан та хуки — щоб розвинути інтуїцію замість запам’ятовування API.
  • React Components 101: створення повторно використовуваних та легко підтримуваних елементів інтерфейсу — Дізнайтеся, чому розділення інтерфейсу на невеликі компоненти React покращує їх повторне використання, читабельність та співпрацю в команді, а потім створіть свій перший функціональний компонент.
  • Увімкнення підтримки офлайн у веб-додатках за допомогою Service Workers — Дізнайтеся, як використовувати Service Workers та API Cache для миттєвого завантаження веб-сайту та його подальшої роботи навіть без підключення до Інтернету.
  • Справжня складність сучасного JavaScript походить від інструментів, а не від мови — У цій статті пояснюється, як такі основні функції JavaScript, як async/await та optional chaining, спрощують код, тоді як зайві інструменти та залежності створюють непотрібну складність.
  • Розробка в React з використанням ШІ: справжні переваги та обмеження — пояснює, де асистенти з кодування на основі ШІ дійсно прискорюють роботу з React, де вони зазнають невдач, а також пропонує практичний підхід до їх використання без зниження якості коду.