Пошук уроків, статей та іншого контенту
Розберете принципи SPA, компонентного підходу та повторного використання частин інтерфейсу.
SPA, або Single-Page Application, — це вебзастосунок з однією HTML-сторінкою, вміст якої змінюється під час взаємодії з користувачем.
У звичайному багатосторінковому сайті перехід між сторінками зазвичай виглядає так:
Користувач натискає посилання.
Браузер надсилає новий запит до сервера.
Сервер повертає іншу HTML-сторінку.
Браузер повністю перезавантажує документ.
У SPA:
Браузер завантажує основний документ один раз.
React створює інтерфейс у цьому документі.
Під час взаємодії змінюються лише потрібні частини інтерфейсу.
Повного перезавантаження сторінки не відбувається.
Наприклад, у застосунку можна перемикатися між розділами «Головна» та «Про нас». Зміниться вміст сторінки, але сам документ браузера не буде завантажено заново.
React-проєкт зазвичай має кореневий елемент у HTML:
<div id="root"></div>React підключається до цього елемента та рендерить у нього кореневий компонент:
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
const root = createRoot(document.getElementById("root"));
root.render(<App />);Компонент App стає початковою точкою інтерфейсу:
function App() {
return <h1>Мій React-застосунок</h1>;
}
export default App;Після цього React може оновлювати вміст root, не перезавантажуючи всю HTML-сторінку.
Компонентний підхід означає, що інтерфейс складається з невеликих незалежних частин — компонентів.
Компонент може описувати:
кнопку;
картку товару;
заголовок;
навігацію;
форму;
цілу сторінку.
Простий компонент — це функція, яка повертає JSX:
function WelcomeMessage() {
return <p>Вітаємо у застосунку!</p>;
}Компонент можна використати всередині іншого компонента:
function App() {
return (
<main>
<h1>Головна сторінка</h1>
<WelcomeMessage />
</main>
);
}Назви React-компонентів мають починатися з великої літери. Це допомагає React відрізняти компоненти від звичайних HTML-тегів.
// Компонент
<UserCard />
// HTML-елемент
<section />Великий компонент складніше читати та змінювати. Тому інтерфейс поділяють на логічні частини.
Наприклад, сторінку можна представити так:
App
├── Header
├── Navigation
└── MainContent
└── ProductCardКожна частина відповідає за свою ділянку інтерфейсу:
function Header() {
return <header>Мій магазин</header>;
}
function Navigation() {
return (
<nav>
<a href="#home">Головна</a>
<a href="#products">Товари</a>
</nav>
);
}
function MainContent() {
return (
<main>
<h1>Популярні товари</h1>
</main>
);
}
function App() {
return (
<>
<Header />
<Navigation />
<MainContent />
</>
);
}Такий код легше підтримувати: якщо потрібно змінити навігацію, достатньо працювати з компонентом Navigation.
Компонент можна відобразити багато разів. Це дозволяє не дублювати однакову розмітку.
Наприклад, замість написання трьох карток вручну можна створити один компонент ProductCard:
function ProductCard() {
return (
<article>
<h2>Навушники</h2>
<p>Ціна: 1500 грн</p>
</article>
);
}
function App() {
return (
<main>
<ProductCard />
<ProductCard />
<ProductCard />
</main>
);
}У цьому прикладі картки мають однаковий вміст. Щоб зробити їх різними, компоненту передають дані через props.
props — це вхідні дані компонента. Вони передаються від батьківського компонента до дочірнього.
function ProductCard({ name, price }) {
return (
<article>
<h2>{name}</h2>
<p>Ціна: {price} грн</p>
</article>
);
}
function App() {
return (
<main>
<ProductCard name="Навушники" price={1500} />
<ProductCard name="Клавіатура" price={2300} />
</main>
);
}У компоненті ProductCard значення name і price отримуються через параметр функції.
Рядкові значення можна передавати без фігурних дужок:
<ProductCard name="Навушники" />Числа, змінні та вирази передаються у фігурних дужках:
<ProductCard price={1500} />Props допомагають створювати універсальні компоненти. Компонент описує структуру, а конкретні дані передаються під час використання.
У наступному прикладі:
App є кореневим компонентом;
Header відповідає за заголовок;
Navigation відображає кнопки навігації;
FeatureCard є повторно використовуваним компонентом;
перемикання розділів відбувається без перезавантаження сторінки.
Цей приклад можна зберегти як index.html і відкрити в браузері.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Проста SPA на React</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f6f8;
color: #1f2933;
}
header {
padding: 24px;
background: #2563eb;
color: white;
}
nav {
display: flex;
gap: 8px;
padding: 16px 24px;
background: white;
border-bottom: 1px solid #d9e0e7;
}
button {
padding: 8px 14px;
border: 1px solid #2563eb;
border-radius: 6px;
background: white;
color: #2563eb;
cursor: pointer;
}
button.active,
button:hover {
background: #2563eb;
color: white;
}
main {
max-width: 800px;
margin: 0 auto;
padding: 24px;
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.card {
padding: 16px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
}
</style>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState } = React;
function Header() {
return (
<header>
<h1>Навчальна SPA</h1>
<p>Приклад компонентного підходу в React</p>
</header>
);
}
function Navigation({ currentPage, onPageChange }) {
return (
<nav>
<button
className={currentPage === "home" ? "active" : ""}
onClick={() => onPageChange("home")}
>
Головна
</button>
<button
className={currentPage === "about" ? "active" : ""}
onClick={() => onPageChange("about")}
>
Про застосунок
</button>
</nav>
);
}
function FeatureCard({ title, description }) {
return (
<article className="card">
<h3>{title}</h3>
<p>{description}</p>
</article>
);
}
function HomePage() {
return (
<section>
<h2>Головна сторінка</h2>
<p>Цей розділ відображається без перезавантаження документа.</p>
<div className="cards">
<FeatureCard
title="SPA"
description="Інтерфейс оновлюється без повного перезавантаження сторінки."
/>
<FeatureCard
title="Компоненти"
description="Інтерфейс складається з невеликих незалежних частин."
/>
<FeatureCard
title="Повторне використання"
description="Один компонент можна використати з різними даними."
/>
</div>
</section>
);
}
function AboutPage() {
return (
<section>
<h2>Про застосунок</h2>
<p>
Ця сторінка є окремим компонентом, який React показує за вибором
користувача.
</p>
</section>
);
}
function App() {
const [currentPage, setCurrentPage] = useState("home");
return (
<>
<Header />
<Navigation
currentPage={currentPage}
onPageChange={setCurrentPage}
/>
<main>
{currentPage === "home" ? <HomePage /> : <AboutPage />}
</main>
</>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
</script>
</body>
</html>App зберігає назву поточного розділу:
const [currentPage, setCurrentPage] = useState("home");Значення currentPage спочатку дорівнює "home".
Компонент Navigation отримує:
currentPage — поточний розділ;
onPageChange — функцію для зміни розділу.
Коли користувач натискає кнопку, викликається функція:
onClick={() => onPageChange("about")}Після зміни значення React повторно обчислює потрібну частину інтерфейсу:
{currentPage === "home" ? <HomePage /> : <AboutPage />}Якщо поточний розділ — "home", відображається HomePage. В іншому випадку — AboutPage.
Увесь документ не перезавантажується. React змінює лише вміст, який залежить від currentPage.
Компоненти можна вкладати один в один. Такий підхід називається композицією.
function Layout({ children }) {
return (
<div>
<header>Заголовок</header>
<main>{children}</main>
</div>
);
}
function App() {
return (
<Layout>
<h1>Вміст сторінки</h1>
<p>Цей вміст передано в Layout.</p>
</Layout>
);
}Спеціальний prop children містить усе, що передано між відкривальним і закривальним тегами компонента:
<Layout>
{/* Це значення children */}
</Layout>Композиція дозволяє створювати спільний каркас для різних сторінок і вставляти в нього різний вміст.
Окремий компонент доречно створити, якщо:
частина інтерфейсу повторюється;
частина має власну зрозумілу роль;
компонент можна описати короткою назвою;
окрему частину потрібно змінювати або тестувати незалежно;
основний компонент став надто великим.
Не потрібно розділяти абсолютно кожен HTML-елемент на окремий компонент. Надмірна кількість дрібних компонентів може ускладнити розуміння структури.
Неправильно:
function userCard() {
return <div>Картка</div>;
}
function App() {
return <userCard />;
}React сприйме userCard як назву невідомого HTML-тега, а не як компонент.
Правильно:
function UserCard() {
return <div>Картка</div>;
}
function App() {
return <UserCard />;
}Непрактично:
function App() {
return (
<main>
<article>
<h2>Перший товар</h2>
<p>100 грн</p>
</article>
<article>
<h2>Другий товар</h2>
<p>200 грн</p>
</article>
</main>
);
}Краще винести спільну структуру в компонент і передавати дані через props:
function Product({ name, price }) {
return (
<article>
<h2>{name}</h2>
<p>{price} грн</p>
</article>
);
}
function App() {
return (
<main>
<Product name="Перший товар" price={100} />
<Product name="Другий товар" price={200} />
</main>
);
}Props призначені для читання. Дочірній компонент не повинен безпосередньо змінювати отримане значення.
Замість зміни props дочірній компонент може викликати функцію, яку передав батьківський компонент:
function Button({ onClick }) {
return <button onClick={onClick}>Натиснути</button>;
}
function App() {
function handleClick() {
console.log("Кнопку натиснуто");
}
return <Button onClick={handleClick} />;
}Якщо весь інтерфейс потрібно щоразу завантажувати з сервера, перевага SPA втрачається. Для простого перемикання вже завантажених частин інтерфейсу React може змінювати компоненти без перезавантаження документа.
Якщо один компонент містить заголовок, навігацію, список, картки та багато умов, його складно підтримувати. Частини з окремою відповідальністю краще винести в дочірні компоненти.
SPA завантажує основний документ один раз і оновлює інтерфейс без повного перезавантаження.
React будує інтерфейс із компонентів.
Компонент — це функція, яка повертає JSX.
Компоненти мають починатися з великої літери.
Props передають дані від батьківського компонента до дочірнього.
Один компонент можна багаторазово використовувати з різними props.
Композиція допомагає вкладати компоненти та створювати спільний каркас інтерфейсу.
Поділ інтерфейсу на компоненти робить код зрозумілішим і зменшує дублювання.