Ось готовий урок, згенерований у стилі Девіда Малана (CS50) спеціально для тебе. Ми розберемо Композицію компонентів — тему, яка перетворює хаос коду на елегантну архітектуру.
🎓 Урок: Композиція компонентів
(Або як перестати будувати будинки з піску і почати будувати з LEGO)
👋 Привіт, друзі! Радий бачити вас тут.
Сьогодні ми поговоримо про дещо фундаментальне. Дещо, що відрізняє новачка, який пише "спагеті-код", від інженера, який будує системи рівня Facebook чи Netflix. Це — Композиція.
1. 🔥 Вступ: Проблема та мотивація
Уявіть собі ситуацію. Ви вирішили побудувати власний інтернет-магазин. Ви відкриваєте редактор коду і починаєте писати HTML.
Спочатку ви робите картку товару для iPhone: картинка, заголовок, ціна, кнопка "Купити". Чудово!
Але потім вам потрібно додати ще 50 товарів. Що ви робите? 1. Копіюєте код картки 50 разів? 2. А якщо завтра дизайнер скаже: "Слухай, давай зробимо всі кнопки "Купити" не синіми, а зеленими і круглими?"
Вам доведеться лізти в 50 різних місць і змінювати код вручну? Це пекло. Це шлях до помилок.
А тепер уявіть LEGO. Коли ви будуєте замок з LEGO, ви ж не відливаєте кожну стіну з рідкого пластику щоразу заново, правда? Ви берете готові "цеглинки". Ви берете "вікно", берете "двері" і вставляєте їх у "стіну".
Ось у чому суть: У програмуванні ми хочемо того ж самого. Ми хочемо створити компонент "Кнопка" один раз і використовувати його скрізь. Ми хочемо вкладати компоненти один в одного, як матрьошки.
Питання до вас: Якби ви будували автомобіль, ви б хотіли винаходити двигун заново для кожної моделі, чи просто взяти готовий двигун і вставити його в новий корпус?
Сьогодні ми навчимося "вставляти двигуни".
2. 🧠 Теоретична база (без нудьги)
Що ж таке Композиція компонентів?
Простими словами: це процес створення складних компонентів шляхом об'єднання простіших. Це принцип "Розділяй і володарюй".
Як це працює "під капотом"?
Уявіть дерево. У нас є корінь (весь додаток), гілки (великі розділи: хедер, сайдбар, контент) і листя (кнопки, іконки, текст).
Коли ми робимо композицію, ми кажемо Батьківському компоненту (Parent): "Гей, у тебе всередині буде ось цей Дочірній компонент (Child)".
🔑 Два типи композиції, які треба зрозуміти:
-
Containment (Вкладення / Контейнери): Це як коробка. Коробці байдуже, що в ній лежить — піца, взуття чи кіт. У коді це часто реалізується через спеціальну пропсу (в React це
children, у Vue —slots). Приклад: Модальне вікно (Modal), яке просто показує те, що ви в нього передали. -
Specialization (Спеціалізація): Це коли ми беремо загальний компонент і робимо його "особливу" версію. Приклад: Є звичайна
Button. Ми створюємоDeleteButton, яка використовує всерединіButton, але завжди червона і має іконку смітника.
❗️ Що запам'ятати залізно: Компоненти не повинні знати надто багато про своїх "дітей". Чим менше вони знають, тим вони універсальніші.
3. 🧪 Приклади (від простого до реального)
Давайте подивимось на код (на прикладі React/JSX, бо це стандарт індустрії, але логіка однакова скрізь).
Рівень 1: Мінімальний приклад (Спеціалізація)
У нас є проста кнопка.
// Базовий компонент
function Button({ color, text }) {
return <button style={{ backgroundColor: color }}>{text}</button>;
}
// Композиція: ми створюємо "Небезпечну Кнопку" з "Звичайної Кнопки"
function DangerButton() {
return <Button color="red" text="Видалити все!" />;
}
Питання: Що буде, якщо ми змінимо розмір шрифту в компоненті Button?
Відповідь: Він автоматично зміниться і в DangerButton. Магія! ✨
Рівень 2: Реальний приклад (Вкладення / Children)
Тепер цікавіше. Уявіть, що ми робимо красиву рамку для фото або тексту.
Що ви очікуєте? Ми хочемо написати компонент-обгортку, в який можна запхати будь-що.
// FancyBorder не знає, що буде всередині. Він просто малює рамку.
function FancyBorder(props) {
return (
<div className={'fancy-border fancy-border-' + props.color}>
{/* Ось тут магія! props.children — це те, що ми вкладемо всередину */}
{props.children}
</div>
);
}
// Використання:
function WelcomeDialog() {
return (
<FancyBorder color="blue">
{/* Ми передаємо заголовок і параграф ЯК ДІТЕЙ */}
<h1 className="Dialog-title">Ласкаво просимо!</h1>
<p className="Dialog-message">Дякуємо, що завітали на наш урок.</p>
</FancyBorder>
);
}
Чому це круто? FancyBorder відповідає тільки за рамку. Йому байдуже, чи там текст, чи картинка, чи інша форма.
Рівень 3: Складніша композиція (Слот-патерн)
Іноді нам треба покласти щось "наліво", а щось "направо".
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left} {/* Слот для лівої частини */}
</div>
<div className="SplitPane-right">
{props.right} {/* Слот для правої частини */}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={<Contacts />}
right={<Chat />}
/>
);
}
Ми зібрали макет сторінки, просто передавши компоненти як пропси!
4. 🛠 Практична частина
Час забруднити руки кодом! Відкрийте свій редактор (або CodeSandbox).
Завдання 1: Розігрів
Скопіюйте приклад Button та DangerButton. Створіть третій компонент SuccessButton (зелений, текст "Готово"), використовуючи той самий базовий Button.
Завдання 2: "Конструктор LEGO"
Створіть компонент Card (картка). Він має приймати children.
* Додайте стилі до Card (тінь, заокруглені кути, відступи).
* Використайте Card тричі:
1. Для відображення тексту.
2. Для відображення картинки.
3. Для відображення іншої Card всередині (Картка в картці).
Завдання 3: Виправлення помилки У вас є код:
function UserLayout() {
return (
<Navbar />
<Sidebar />
<Feed />
);
}
Це не спрацює (принаймні в React). Чому? Як виправити це, використовуючи композицію або фрагменти?
Завдання 4: Міні-кейс "Сайт новин"
Створіть структуру сторінки, використовуючи компоненти:
* Header (містить Логотип і Меню)
* Layout (приймає children)
* Article (приймає заголовок і текст)
Зберіть це разом так, щоб у App ви виглядали приблизно так:
<Layout><Header /><Article ... /></Layout>
Завдання 5: А що, якщо...
Спробуйте передати в компонент Button не просто текст, а іконку (emoji) разом з текстом через children, а не через проп text. Перепишіть компонент Button, щоб він підтримував це.
5. 💡 Мислення як у розробника
Як відрізнити гарну композицію від поганої?
🚫 Типова помилка новачка: "God Component" (Божественний об'єкт)
Новачки часто створюють один компонент UserProfile, де пишуть 500 рядків коду: і верстку аватара, і логіку кнопки, і форматування адреси.
Не робіть так. Якщо ви бачите, що компонент займає більше 100-150 рядків — це сигнал тривоги. Розбивайте його!
🚫 Помилка: Prop Drilling (Буріння пропсів)
Це коли ви передаєте дані через 10 компонентів вниз, хоча проміжні компоненти ці дані не використовують. Рішення: Використовуйте композицію (вкладення), щоб передати компонент відразу туди, де він потрібен.
🧠 Як думає Senior:
"Я бачу цю кнопку в дизайні вже втретє. Я не буду її копіювати. Я винесу її в окремий модуль
UI/Button. А цей складний віджет я розіб'ю наHeader,BodyіFooter, щоб потім можна було легко змінитиHeader, не ламаючиFooter."
Золоте правило: Компонент має робити одну річ, але робити її добре. (Single Responsibility Principle).
6. 🧩 Підсумок
Отже, що ми сьогодні зробили? Ми відмовилися від монолітного, страшного коду. Ми навчилися: 1. Створювати будівельні блоки (компоненти). 2. Вкладати їх один в одного (children/slots). 3. Спеціалізувати їх (робити унікальні версії загальних речей).
Тепер ви можете будувати складні інтерфейси, зберігаючи код чистим і зрозумілим. Ви будуєте своє місто з LEGO, а не ліпите його з бруду.
🤔 Тизер наступного уроку:
Добре, у нас є красиві компоненти. Але вони "німі". Кнопка натискається, але нічого не відбувається. Як змусити їх "говорити" один з одним? Як передати сигнал від кнопки "Купити" до іконки "Кошик"?
На наступному уроці ми поговоримо про State (Стан) та Потік Даних. Це буде 🔥.
А поки що — практикуйтесь! Код сам себе не напише. Успіхів!