Модуль 20

Loading, error та empty states

Ось готовий урок, створений спеціально за твоїм майстер-промптом. Це не просто лекція, це — перформанс у стилі CS50.


🎓 Урок: Мистецтво зворотного зв'язку: Loading, Error та Empty states

Привіт, друзі! Це CS50 (умовно 😉), і сьогодні ми говоримо про дещо критично важливе. Про те, що відрізняє професійний застосунок від студентської поробки.

1. 🔥 Вступ: Чому мовчання — це не золото

Уявіть ситуацію. Ви заходите в кафе. Підходите до бариста і замовляєте: "Мені, будь ласка, подвійний еспресо".

Бариста дивиться на вас. Мовчки. Він не киває, не починає готувати каву, не каже "Зачекайте хвилинку". Він просто стоїть і дивиться крізь вас. Минає 5 секунд. 10 секунд. Вам стає ніяково. Ви думаєте: "Він мене почув? Може, він завис? Може, кафе зачинене, а я не помітив?"

Врешті-решт ви просто йдете геть.

Риторичне запитання: Скільки разів ви натискали кнопку в додатку, нічого не відбувалося, і ви натискали її ще раз, і ще, і ще?

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

Сьогодні ми навчимося робити ваші програми "ввічливими". Ми розберемо три стани, без яких не обходиться жоден сучасний інтерфейс: 1. Loading (Я працюю над цим). 2. Error (Вибач, щось пішло не так). 3. Empty (Я подивився, але там нічого немає).


2. 🧠 Теоретична база: Що там "під капотом"?

Давайте заглянемо всередину. Коли ви відкриваєте Instagram, стрічка не з'являється магічним чином миттєво. Ваша програма (клієнт) робить запит до серверу. Це займає час.

У програмуванні це називається асинхронністю. І поки ми чекаємо, наш інтерфейс перебуває в певному стані (state).

Три кити UX (User Experience):

  1. Loading State (Стан завантаження)

    • Що це: Індикатор того, що процес іде.
    • Мета: Заспокоїти користувача: "Не йди, ми тут, ми працюємо".
    • Приклади: Спінер (колесо, що крутиться), прогрес-бар або, що зараз модно, Skeleton (сірі прямокутники, що імітують контент).
  2. Error State (Стан помилки)

    • Що це: Повідомлення про те, що запит провалився.
    • Чому це важливо: Інтернет зникає. Сервери падають. Це нормально. Ненормально — залишати білий екран.
    • Логіка: Ми повинні сказати ЩО сталося і ЯК це виправити (кнопка "Спробувати ще").
  3. Empty State (Пустий стан)

    • Що це: Це успішний запит, який повернув... нічого.
    • Приклад: Ви шукаєте "піца з ананасами", а в меню її немає. Результат — 0 товарів.
    • Помилка новачків: Показати пустий білий екран. Це збиває з пантелику. Треба написати: "Нічого не знайдено".

💡 Що треба запам'ятати (Інтуїція):

Уявіть змінну status. Вона може бути: 'idle' (очікування), 'loading', 'success', або 'error'. Ваш код — це просто світлофор, який перемикає кольори (екрани) залежно від цього статусу.


3. 🧪 Приклади: Від простого до реального

Давайте напишемо це псевдокодом (схожим на React/JS), щоб зрозуміти логіку.

Приклад 1: "Щасливий шлях" (The Happy Path)

Це те, як пишуть код новачки. Вони вірять, що інтернет ідеальний.

function UserProfile() {
  const data = fetchData(); // Магічно отримуємо дані

  // Ми просто показуємо дані.
  // А що, якщо вони ще не прийшли? Помилка!
  return <div>Привіт, {data.name}!</div>; 
}

Питання до вас: Що побачить користувач, якщо fetchData займе 3 секунди? Відповідь: Скоріш за все, програма впаде ("Cannot read property 'name' of undefined") або покаже пусте місце.


Приклад 2: Додаємо Loading та Error

Тепер мислимо як інженери.

function UserProfile() {
  // У нас є три змінні стану
  const isLoading = ...; // чи вантажиться?
  const error = ...;     // чи є помилка?
  const data = ...;      // самі дані

  // 1. Спочатку перевіряємо завантаження
  if (isLoading) {
    return <Spinner />; // 🔄 Крутилка
  }

  // 2. Потім перевіряємо помилку
  if (error) {
    return (
      <div>
        <p>Ой! Не вдалося завантажити профіль.</p>
        <button>Спробувати ще раз</button>
      </div>
    );
  }

  // 3. Якщо дійшли сюди — у нас є дані!
  return <div>Привіт, {data.name}!</div>;
}

Бачите? Це як фільтр. Ми відсіюємо стани один за одним.


Приклад 3: Підступний Empty State

Уявіть, що ми завантажуємо список повідомлень.

Питання: Ми успішно завантажили дані, помилок немає. Але масив повідомлень порожній []. Що покаже код із прикладу 2? Відповідь: Він покаже просто порожній блок <div></div>. Це погано. Користувач подумає, що зламався інтерфейс.

Виправляємо:

function MessageList() {
  if (isLoading) return <SkeletonList />;
  if (error) return <ErrorMessage />;

  // 🔥 Перевірка на Empty State
  if (data.length === 0) {
    return (
      <div class="empty-state">
        <img src="sad-box.png" />
        <h3>Повідомлень немає</h3>
        <p>Напишіть першим!</p> {/* Заклик до дії! */}
      </div>
    );
  }

  // Якщо повідомлення є
  return data.map(msg => <Message text={msg} />);
}

4. 🛠 Практична частина

Час забруднити руки кодом! (Можна писати псевдокодом або словами).

Завдання 1: Детектив У вас є інтернет-магазин. Користувач вводить у пошук "IPhone 15". Сервер відповідає кодом 200 OK, але список товарів items: []. Який стан ви маєте показати? * Loading * Error * Empty * Success (але який саме?)

Завдання 2: "Моргання" Ви зробили лоадер. Але дані завантажилися дуже швидко (за 0.1 сек). Лоадер з'явився на мить і зник. Це виглядає як "глюк" (моргання інтерфейсу). Як би ви вирішили цю проблему логічно?

Завдання 3: Реальний кейс Спроєктуйте Error State для ситуації: "У користувача зник інтернет під час відправки важливого коментаря". * Що ви йому напишете? * Яку кнопку дасте? (Просто "Ок" чи щось краще?) * Чи збережете ви його текст, чи він зникне?

Завдання 4: Bug fix Знайдіть логічну помилку в цьому коді:

if (data) {
  return <List items={data} />;
} 
if (isLoading) {
  return <Spinner />;
}

(Підказка: Що, якщо data вже є з минулого разу, але ми почали оновлювати її і isLoading знову став true?)


5. 💡 Мислення як у розробника

Ось що відрізняє сеньйора від джуніора:

  1. Джуніор думає про "Happy Path" (коли все працює). Він малює інтерфейс для ідеальних даних.
  2. Сеньйор починає з "Edge Cases" (крайніх випадків). Він питає: "А що, якщо даних буде нуль? А що, якщо мільйон? А що, якщо сервер впаде?"

Типові помилки: * Вічний спінер: Коли сталася помилка, але ви забули перемкнути isLoading в false. Користувач буде дивитися на колесо вічно. * Страшні помилки: Виводити користувачеві технічний текст: Error 500: NullPointerException. * Як треба: "Наші сервери взяли перерву на каву. Спробуйте пізніше." * Агресивний Empty State: Писати просто "Даних немає". Це нудно. Використовуйте цей стан, щоб навчити користувача або підштовхнути до дії ("Додайте свій перший товар!").

Професійна порада: Використовуйте Skeletons замість спінерів. Спінер привертає увагу до самого процесу очікування. Скелетон створює ілюзію, що контент вже майже тут. Це психологія!


6. 🧩 Підсумок

Отже, друзі!

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

Тепер ви вмієте: ✅ Розрізняти Loading, Error та Empty стани. ✅ Розуміти, чому не можна просто показати пустий екран. ✅ Проєктувати логіку відображення даних більш надійно.

Наступного разу ми підемо ще далі. Що, якщо ми покажемо користувачеві результат ще до того, як сервер відповість? Це називається Optimistic UI. І це справжня магія.

А поки що — це був CS50. (І не забудьте обробити помилки у своєму домашньому завданні!) 🚀