Пошук уроків, статей та іншого контенту
Розберете анатомію React-компонента, його роль у UI та правила створення повторно використовуваних частин інтерфейсу.
React-компонент — це незалежна частина інтерфейсу, яку можна створювати, використовувати та комбінувати з іншими частинами.
Компонент зазвичай:
описує, який HTML потрібно показати;
отримує дані від батьківського компонента;
може використовуватися багато разів;
приховує деталі реалізації конкретної частини інтерфейсу.
Наприклад, сторінка інтернет-магазину може складатися з таких компонентів:
Header — верхня панель;
ProductList — список товарів;
ProductCard — картка одного товару;
Footer — нижня частина сторінки.
Замість великого компонента зі всією розміткою застосунок розділяють на менші зрозумілі частини.
У сучасному React компоненти зазвичай створюють як JavaScript-функції:
function Greeting() {
return <h1>Вітаємо в застосунку!</h1>;
}Цей компонент складається з таких частин:
function Greeting() — оголошення функції компонента.
return — значення, яке компонент відображає.
<h1>...</h1> — JSX-розмітка.
Greeting — назва компонента.
Назви React-компонентів повинні починатися з великої літери. Це допомагає React відрізняти компоненти від звичайних HTML-тегів:
function Header() {
return <header>Шапка сайту</header>;
}Якщо назвати компонент з малої літери, React сприйматиме його як HTML-тег:
// Неправильно для React-компонента
function header() {
return <header>Шапка сайту</header>;
}JSX — це синтаксис, який дає змогу описувати інтерфейс у JavaScript-коді. JSX схожий на HTML, але має кілька правил.
Компонент повинен повертати один кореневий елемент. Ним може бути звичайний HTML-елемент або фрагмент <>...</>:
function UserInfo() {
return (
<section>
<h2>Олена</h2>
<p>Frontend-розробниця</p>
</section>
);
}Якщо окремий контейнер не потрібен, використовуйте фрагмент:
function UserInfo() {
return (
<>
<h2>Олена</h2>
<p>Frontend-розробниця</p>
</>
);
}У JSX кожен тег повинен бути закритий:
function Avatar() {
return <img src="/avatar.png" alt="Аватар користувача" />;
}Порожні елементи, наприклад img або input, записують із самозакривним слешем.
Частина атрибутів у JSX записується у форматі JavaScript. Наприклад, замість class використовується className:
function Button() {
return (
<button className="primary-button" type="button">
Зберегти
</button>
);
}Після оголошення компонент використовують як спеціальний JSX-тег:
function Header() {
return <header>Мій застосунок</header>;
}
function App() {
return (
<main>
<Header />
<p>Вміст сторінки</p>
</main>
);
}<Header /> викликає компонент Header, а React вставляє його результат у потрібне місце.
Компоненти можна вкладати один в одного. Компонент, який використовує інший компонент, називають батьківським. Використаний компонент є дочірнім.
У цьому прикладі:
App — батьківський компонент для Header;
Header — дочірній компонент для App.
Компоненти часто потрібно використовувати з різними даними. Для цього застосовують props — властивості компонента.
Пропси передають у JSX як атрибути:
function UserCard({ name, role }) {
return (
<article>
<h2>{name}</h2>
<p>{role}</p>
</article>
);
}
function App() {
return (
<main>
<UserCard name="Олена" role="Frontend-розробниця" />
<UserCard name="Андрій" role="Backend-розробник" />
</main>
);
}Компонент UserCard отримує об’єкт із пропсами:
{
name: "Олена",
role: "Frontend-розробниця"
}У параметрах функції ці властивості можна одразу деструктурувати:
function UserCard({ name, role }) {
// name і role отримані з props
return (
<article>
<h2>{name}</h2>
<p>{role}</p>
</article>
);
}Альтернативний запис виглядає так:
function UserCard(props) {
return (
<article>
<h2>{props.name}</h2>
<p>{props.role}</p>
</article>
);
}Деструктуризація зазвичай робить код коротшим і зручнішим для читання.
JavaScript-вирази вставляють у JSX за допомогою фігурних дужок:
function ProductCard({ title, price }) {
return (
<article>
<h2>{title}</h2>
<p>Ціна: {price} грн</p>
</article>
);
}У фігурних дужках можна використовувати змінні, властивості об’єктів і прості вирази:
function ProductCard({ title, price }) {
const formattedPrice = `${price} грн`;
return (
<article>
<h2>{title}</h2>
<p>{formattedPrice}</p>
</article>
);
}Одна з головних переваг компонентів — можливість використовувати їх повторно.
Розглянемо повний приклад застосунку зі спільним компонентом картки товару:
function ProductCard({ title, price, category }) {
return (
<article className="product-card">
<p>{category}</p>
<h2>{title}</h2>
<strong>{price} грн</strong>
</article>
);
}
function ProductList() {
return (
<section>
<h1>Товари</h1>
<ProductCard
title="Механічна клавіатура"
price={3200}
category="Аксесуари"
/>
<ProductCard
title="USB-мікрофон"
price={2800}
category="Аудіо"
/>
<ProductCard
title="Вебкамера"
price={2100}
category="Відео"
/>
</section>
);
}
function App() {
return (
<main>
<ProductList />
</main>
);
}
export default App;Компонент ProductCard описує структуру однієї картки, але не містить конкретних даних. Дані надходять через пропси.
Це краще, ніж тричі копіювати однакову HTML-структуру, тому що:
зміни потрібно робити лише в одному місці;
усі картки мають однакову структуру;
компонент легко використовувати для нових товарів;
батьківський компонент відповідає за дані, а картка — за їх відображення.
Компоненти допомагають поділити інтерфейс за функціональними частинами.
Наприклад, сторінку профілю можна структурувати так:
App
└── ProfilePage
├── Header
├── UserInfo
├── Statistics
└── FooterКожен компонент має свою роль:
ProfilePage об’єднує частини сторінки;
Header показує верхню панель;
UserInfo показує основну інформацію про користувача;
Statistics показує статистику;
Footer показує нижню частину сторінки.
Такий поділ робить структуру застосунку зрозумілішою. Якщо потрібно змінити статистику, не доведеться шукати відповідну частину серед усієї розмітки сторінки.
Під час створення компонента корисно дотримуватися кількох правил.
Компонент повинен мати зрозумілу роль. Наприклад, ProductCard відповідає за відображення товару, а не за всю сторінку магазину.
Невдалий варіант:
function ProductPage() {
// Розмітка шапки, фільтрів, списку товарів і підвалу
return <div>Великий компонент з усією сторінкою</div>;
}Краще розділити сторінку:
function ProductPage() {
return (
<>
<Header />
<ProductFilters />
<ProductList />
<Footer />
</>
);
}Не прив’язуйте повторно використовуваний компонент до одного конкретного значення:
function UserCard() {
return <h2>Олена</h2>;
}Краще зробити ім’я вхідним параметром:
function UserCard({ name }) {
return <h2>{name}</h2>;
}Тепер компонент можна використати для будь-якого користувача:
<UserCard name="Олена" />
<UserCard name="Андрій" />Назва повинна пояснювати, що показує або робить компонент:
function SearchForm() {
return <form>...</form>;
}
function NavigationMenu() {
return <nav>...</nav>;
}
function CommentItem() {
return <article>...</article>;
}Загальні назви на кшталт Block, Part або Component не допомагають зрозуміти призначення елемента.
Якщо кілька елементів мають однакову структуру, створіть для них спільний компонент і передавайте відмінні дані через props.
childrenКомпонент може отримувати не лише звичайні значення, а й інший JSX-вміст. Цей вміст доступний через спеціальний prop children.
function Panel({ title, children }) {
return (
<section className="panel">
<h2>{title}</h2>
<div>{children}</div>
</section>
);
}
function App() {
return (
<Panel title="Профіль користувача">
<p>Ім’я: Олена</p>
<p>Роль: розробниця</p>
</Panel>
);
}
export default App;Усередині Panel:
title — звичайний prop;
children — вміст між відкривальним і закривальним тегами Panel.
Такий підхід дає змогу створити спільну оболонку і вставляти в неї різний вміст.
Важливо розділяти відповідальність між компонентами:
батьківський компонент визначає, які дані передати;
дочірній компонент показує отримані дані;
дочірній компонент не повинен бути прив’язаний до одного конкретного набору значень.
Наприклад:
function StatusMessage({ status }) {
return <p>Статус замовлення: {status}</p>;
}
function OrderPage() {
const orderStatus = "Підтверджено";
return <StatusMessage status={orderStatus} />;
}OrderPage знає, який статус потрібно показати, а StatusMessage знає, як цей статус відобразити.
function userCard() {
return <div>Картка</div>;
}Використовуйте велику літеру:
function UserCard() {
return <div>Картка</div>;
}returnФункція без return нічого не відобразить:
function Header() {
<header>Шапка</header>;
}Правильно:
function Header() {
return <header>Шапка</header>;
}function Card() {
return (
<h2>Товар</h2>
<p>Ціна</p>
);
}Потрібен один кореневий елемент або фрагмент:
function Card() {
return (
<>
<h2>Товар</h2>
<p>Ціна</p>
</>
);
}function Greeting({ name }) {
return <h1>name</h1>;
}У такому випадку буде показано текст name. Щоб вставити значення змінної, використовуйте фігурні дужки:
function Greeting({ name }) {
return <h1>{name}</h1>;
}Якщо одна й та сама структура повторюється кілька разів, її краще винести в окремий компонент. Це зменшує кількість коду та спрощує подальші зміни.
React-компонент — це незалежна частина інтерфейсу.
Компоненти зазвичай створюють як функції, назви яких починаються з великої літери.
Компонент повертає JSX, що описує його UI.
Компоненти можна вкладати один в один і комбінувати для побудови сторінки.
props використовують для передавання даних від батьківського компонента до дочірнього.
children містить JSX, переданий між тегами компонента.
Повторно використовувані компоненти повинні мати зрозумілу відповідальність і отримувати змінні дані через props.
Поділ інтерфейсу на компоненти робить застосунок зрозумілішим і полегшує його підтримку.