Модуль 12

Рендеринг списків і key

Ось готовий урок, написаний у стилі CS50, спеціально для теми "Рендеринг списків і key".


🎓 CS50: Рендеринг списків і таємниця атрибута key

Привіт, друзі! Радий вас бачити. Сьогодні ми зануримося в тему, яка перетворює статичні веб-сторінки на живі, динамічні додатки.

1. 🔥 Вступ: проблема та мотивація

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

У вас є масив даних із сервера — скажімо, 100 постів. Як ви будете їх показувати? Чи будете ви вручну писати в коді: <Post />, потім ще один <Post />, і ще один... і так сто разів?

А що, якщо завтра постів стане 101? Або 50? Або нуль? Ви ж не будете переписувати код програми щоразу, коли хтось завантажує нове фото, правда? Це було б божевіллям.

Тут виникає фундаментальна проблема: Як нам змусити комп'ютер взяти купу "сухих" даних (масив) і автоматично перетворити їх на красиві інтерфейсні елементи (UI), незалежно від того, скільки їх там — один чи мільйон?

І найголовніше — як зробити це так, щоб ваш браузер не "завис", намагаючись оновити цей список?

Ось тут на сцену виходить рендеринг списків і той самий загадковий атрибут key, на який React постійно свариться в консолі червоним текстом. Давайте розберемося, навіщо це потрібно.


2. 🧠 Теоретична база (без сухої академічності)

Метод .map() — ваш конвеєр

У JavaScript (і в React) ми не використовуємо цикли for для рендерингу так часто, як в інших мовах. Ми використовуємо функціональне програмування.

Уявіть собі заводський конвеєр. На вході у вас ящик сирої картоплі (ваш масив даних). На конвеєрі стоїть робот (метод .map()). Він бере кожну картоплину, чистить її, смажить і на виході видає пачку чіпсів (ваш HTML-елемент).

Головне правило: Метод .map() бере масив даних і повертає новий масив, але вже з JSX-елементами.

Таємниця key (Ключ)

Коли ви створюєте список, React (або інша бібліотека) вимагає дати кожному елементу унікальний key. Чому?

Уявіть, що ви — вчитель у класі з 30 учнями. Ви кажете: "Всі пересядьте на одне місце вправо!". Якщо ви знаєте учнів лише за їхнім місцем ("Ей, ти за третьою партою!"), у вас буде хаос. Ви не знаєте, хто куди пересів. Але якщо у кожного учня є унікальне ім'я (ID), то навіть якщо вони поміняються місцями, ви точно знатимете: "Ага, Іван тепер тут, а Марія там".

Як це працює "під капотом"? React використовує механізм Reconciliation (звірки). Він порівнює стару версію списку і нову. * Без key: React думає, що ви видалили старий список і створили новий. Він "зносе" весь DOM і побудує заново. Це повільно. * З key: React бачить: "О, елемент з ID=45 нікуди не зник, він просто переїхав нижче. Я просто пересуну його, не перемальовуючи вміст".

Запам'ятайте: key — це паспорт елемента. Він має бути унікальним серед сусідів і стабільним (не змінюватися при кожному рендері).


3. 🧪 Приклади (від простого до реального)

Приклад 1: Простий список рядків

У нас є список покупок.

const products = ['Хліб', 'Молоко', 'Яблука'];

function ShoppingList() {
  // Ми перетворюємо рядки на <li>
  const listItems = products.map((product) => (
    <li key={product}>
      {product}
    </li>
  ));

  return <ul>{listItems}</ul>;
}

Питання до вас: Чому я використав назву продукту як key? Відповідь: У цьому простому списку назви унікальні. Але якщо ми купимо два види "Хліба", React насварить за дублікат ключа.


Приклад 2: Реальний світ (Масив об'єктів)

У реальних проєктах дані приходять із бази даних і мають id. Це ідеальний кандидат для ключа.

const users = [
  { id: 101, name: "Олена", role: "Admin" },
  { id: 102, name: "Андрій", role: "User" },
  { id: 103, name: "Катя", role: "Editor" }
];

function UserList() {
  return (
    <div className="user-container">
      {users.map((user) => (
        // Зверніть увагу: key ставимо на найвищий елемент всередині map
        <div key={user.id} className="user-card">
          <h3>{user.name}</h3>
          <p>Role: {user.role}</p>
        </div>
      ))}
    </div>
  );
}

Що ви очікуєте побачити? Три картки користувачів. Чому так? React взяв кожен об'єкт, підставив дані в шаблон HTML і використав id для відстеження. Якщо Олена видалить свій акаунт, React видалить із DOM тільки її div, не чіпаючи Андрія та Катю.


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

Час кодити! Відкривайте редактор.

  1. 🔹 Повторення: Створіть масив ["React", "Angular", "Vue"] і виведіть його як маркований список (<ul>).
  2. 🔹 Об'єкти: Створіть масив книг (об'єкти з полями id, title, author). Виведіть їх у вигляді карток.
  3. 🔹 Умови в списку: Використовуючи попередній масив книг, додайте поле isRead: true/false. Відрендеріть список так, щоб прочитані книги мали зелений колір тексту, а непрочитані — червоний.
  4. 🔹 Виправлення помилки: javascript // Знайдіть проблему в цьому коді: const tasks = ["Спати", "Їсти", "Кодити", "Спати"]; tasks.map(task => <li key={task}>{task}</li>); Підказка: Що станеться з ключами, якщо задачі однакові? Як це виправити? (Додайте індекс другим аргументом map або зробіть дані унікальними).
  5. 🔹 Міні-кейс: Уявіть, що ви робите меню для піцерії. Є масив піц. Виведіть тільки ті піци, які вегетаріанські (спочатку filter, потім map).

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

Ось де новачки часто помиляються, а профі — ні.

❌ Помилка №1: Використання index як ключа

Дуже спокусливо написати так: users.map((user, index) => <li key={index}>{user.name}</li>)

Чому це погано? Уявіть чергу в магазин. 1. Андрій (index 0) 2. Оля (index 1)

Якщо Андрій піде, Оля стане першою (index 0). Для React це виглядає так, ніби Оля перетворилася на Андрія, бо ключ 0 залишився, просто змінив контент. Це ламає поля вводу, анімації та сортування. Порада: Використовуйте index тільки якщо список ніколи не буде змінюватися, сортуватися чи фільтруватися. В іншому випадку — вимагайте у бекенда id.

❌ Помилка №2: Генерація ключів "на льоту"

key={Math.random()} Ніколи так не робіть! Це змушує React думати, що при кожному оновленні це зовсім нові елементи. Він буде видаляти старі та створювати нові постійно. Це вбиває продуктивність і втрачає фокус у полях вводу.

✅ Як думає профі:

"Які дані унікально ідентифікують цей елемент у базі даних? ID? URL? SKU товару? Оце і буде моїм ключем".


6. 🧩 Підсумок

Отже, що ми сьогодні розібрали?

  1. Ми не копіюємо код. Ми використовуємо масиви та метод .map(), щоб генерувати інтерфейс динамічно.
  2. key — це не просто "щоб консоль не сварилась". Це критично важливий інструмент для продуктивності та коректності роботи React.
  3. Ключі мають бути стабільними та унікальними серед братів-елементів.

Тепер ви вмієте: Створювати сторінки, які можуть відображати 5 товарів або 5000, використовуючи один і той самий шаблон коду. Ви зробили величезний крок до створення реальних додатків!

🔜 Що далі? Зараз наші списки просто "висять". А що, якщо ми хочемо додати кнопку "Видалити" біля кожного товару або форму для додавання нового? Наступного разу ми поговоримо про State (Стан) та обробку подій, щоб оживити ці списки!

А поки що — це був CS50. Щасти вам у коді!