Композиція проти наслідування в React
Композиція проти Наслідування
React сильно віддає перевагу композиції над наслідуванням. Замість того, щоб розширювати компоненти через ієрархії класів, ви компонуєте їх, комбінуючи менші компоненти разом.
Композиція з дітьми
tsx
// Загальний контейнерний компонент
function Card({ children, title }: { children: React.ReactNode; title: string }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">{children}</div>
</div>
);
}
// Компонуємо різні картки
function App() {
return (
<>
<Card title="Профіль">
<Avatar />
<UserInfo />
</Card>
<Card title="Налаштування">
<SettingsForm />
</Card>
</>
);
}Патерн Спеціалізації
Створіть спеціалізовані версії загального компонента:
tsx
// Загальний діалог
function Dialog({ title, message, children }: {
title: string;
message: string;
children?: React.ReactNode;
}) {
return (
<div className="dialog">
<h1>{title}</h1>
<p>{message}</p>
{children}
</div>
);
}
// Спеціалізовані діалоги через композицію
function WelcomeDialog() {
return (
<Dialog
title="Ласкаво просимо!"
message="Дякуємо, що приєдналися до нашої платформи."
/>
);
}
function DeleteConfirmDialog({ onConfirm }: { onConfirm: () => void }) {
return (
<Dialog title="Видалити?" message="Цю дію не можна скасувати.">
<button onClick={onConfirm}>Так, видалити</button>
</Dialog>
);
}Патерн Слотів (Іменована Композиція)
tsx
function Layout({
header,
sidebar,
content,
footer
}: {
header: React.ReactNode;
sidebar: React.ReactNode;
content: React.ReactNode;
footer: React.ReactNode;
}) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{content}</main>
<footer>{footer}</footer>
</div>
);
}
// Використання іменованих слотів
<Layout
header={<Navbar />}
sidebar={<Sidebar />}
content={<MainContent />}
footer={<Footer />}
/>Чому НЕ Наслідування?
tsx
// ❌ Наслідування — НЕ робіть цього в React
class Button extends React.Component { /* ... */ }
class PrimaryButton extends Button { /* ... */ }
class DangerButton extends PrimaryButton { /* ... */ }
// Жорстка ієрархія, важко змінювати
// ✅ Композиція — РОБІТЬ це замість цього
function Button({ variant, children, ...props }: ButtonProps) {
return <button className={`btn btn-${variant}`} {...props}>{children}</button>;
}
<Button variant="primary">Зберегти</Button>
<Button variant="danger">Видалити</Button>Резюме Патернів Композиції
| Патерн | Як | Сценарій використання |
|---|---|---|
| children | Передайте JSX як дітей | Загальні контейнери, обгортки |
| Спеціалізація | Попередньо заповніть props загального компонента | Специфічний діалог, варіанти кнопок |
| Слоти | Іменовані props для різних областей | Макети, складний UI |
| Render Props | Передайте функцію як prop | Динамічна логіка рендерингу |
| HOC | Оберніть компонент іншим | Перехресні питання |
Важливо:
Команда React рекомендує композицію над наслідуванням в усіх випадках. Використовуйте children, іменовані props (слоти) та композицію компонентів для створення гнучких UI. Наслідування створює жорсткі ієрархії, які важко рефакторити — композиція зберігає компоненти незалежними та повторно використовуваними.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.