Урок 27 из 49 · Месяц 4. React
Компоненты, пропсы, импорт и экспорт, Ant Design
Содержание урока
- Компоненты в отдельных файлах
- Import и export компонентов
- Экспорт по умолчанию (default)
- Именованный экспорт (named)
- Пропсы: передаём данные в компонент
- Деструктуризация пропсов
- Значения по умолчанию
- Какие типы можно передавать
- Пропсы только для чтения
- Специальный проп children
- Композиция: компоненты внутри компонентов
- Ant Design: готовые компоненты
- Установка
- Первые компоненты
- Карточка товара на antd
- Своя тема через ConfigProvider
- Типичные ошибки
- Практика
- Итоги
В прошлом уроке все компоненты жили в одном файле и всегда показывали одно и то же. Теперь научимся раскладывать компоненты по отдельным файлам, импортировать и экспортировать их, а главное — передавать в них данные через пропсы. В конце подключим Ant Design — готовую библиотеку красивых компонентов, чтобы не верстать каждую кнопку с нуля.
Компоненты в отдельных файлах
Когда компонентов становится десяток, держать их в App.jsx неудобно: файл разрастается, трудно найти нужное. Правило простое: один компонент — один файл, имя файла совпадает с именем компонента.
src/
├── components/
│ ├── Header.jsx
│ ├── Footer.jsx
│ └── UserCard.jsx
├── App.jsx
└── main.jsx
Создадим src/components/Header.jsx:
function Header() {
return (
<header className="header">
<h1>Atlas Shop</h1>
</header>
);
}
export default Header;
И используем его в App.jsx:
import Header from './components/Header.jsx';
function App() {
return (
<>
<Header />
<p>Добро пожаловать в магазин!</p>
</>
);
}
export default App;
Import и export компонентов
Вы уже встречали модули в JavaScript. В React они используются постоянно. Есть два вида экспорта.
Экспорт по умолчанию (default)
В файле может быть только один export default. При импорте имя можно выбрать любое, фигурные скобки не нужны.
// Footer.jsx
export default function Footer() {
return <footer>© 2026</footer>;
}
// App.jsx
import Footer from './components/Footer.jsx';
import MyFooter from './components/Footer.jsx'; // тоже сработает — это тот же компонент
Именованный экспорт (named)
Таких экспортов в файле может быть сколько угодно. Импортировать нужно в фигурных скобках и точно по имени.
// components/Buttons.jsx
export function PrimaryButton() {
return <button className="btn btn-primary">Купить</button>;
}
export function SecondaryButton() {
return <button className="btn">Подробнее</button>;
}
// App.jsx
import { PrimaryButton, SecondaryButton } from './components/Buttons.jsx';
import { PrimaryButton as BuyButton } from './components/Buttons.jsx'; // переименование через as
export default |
export (именованный) |
|
|---|---|---|
| Сколько в файле | Один | Сколько угодно |
| Импорт | import Header from './Header' |
import { Header } from './Header' |
| Имя при импорте | Любое | Точно как при экспорте (или через as) |
| Когда удобно | Один главный компонент в файле | Несколько мелких компонентов или утилит |
Пропсы: передаём данные в компонент
Представьте карточку пользователя. Если имя написано прямо внутри, каждая карточка будет одинаковой. Нам же нужна одна «форма» карточки, в которую подставляются разные данные. Для этого существуют пропсы (от англ. properties — свойства).
Аналогия: функция и её аргументы. Компонент — функция, пропсы — аргументы. Передаются пропсы как атрибуты тега:
<UserCard name="Айгерим" age={21} city="Алматы" />
React соберёт все атрибуты в один объект и передаст его первым аргументом в функцию:
// components/UserCard.jsx
function UserCard(props) {
console.log(props); // { name: 'Айгерим', age: 21, city: 'Алматы' }
return (
<div className="user-card">
<h3>{props.name}</h3>
<p>Возраст: {props.age}</p>
<p>Город: {props.city}</p>
</div>
);
}
export default UserCard;
// App.jsx
import UserCard from './components/UserCard';
function App() {
return (
<div>
<UserCard name="Айгерим" age={21} city="Алматы" />
<UserCard name="Данияр" age={25} city="Астана" />
<UserCard name="Мария" age={19} city="Шымкент" />
</div>
);
}
export default App;
Один компонент — три разные карточки.
Деструктуризация пропсов
Писать props. каждый раз утомительно. Поэтому пропсы почти всегда деструктурируют прямо в параметрах (вспомните урок Деструктуризация, spread, rest):
function UserCard({ name, age, city }) {
return (
<div className="user-card">
<h3>{name}</h3>
<p>Возраст: {age}</p>
<p>Город: {city}</p>
</div>
);
}
Это ровно то же самое, только короче и сразу видно, какие пропсы ожидает компонент.
Значения по умолчанию
Если проп не передали, он будет undefined. Значение по умолчанию задаётся прямо в деструктуризации:
function Button({ text = 'Нажми меня', color = 'blue' }) {
return <button style={{ background: color, color: 'white' }}>{text}</button>;
}
// <Button /> → «Нажми меня», синяя
// <Button text="Отправить" /> → «Отправить», синяя
// <Button text="Удалить" color="red" /> → «Удалить», красная
Какие типы можно передавать
Строки передают в кавычках, всё остальное — в фигурных скобках:
<Product
title="Ноутбук" // строка
price={350000} // число
inStock={true} // логическое значение
isNew // то же, что isNew={true}
tags={['новинка', 'хит']} // массив
onBuy={() => alert('Куплено!')} // функция
/>
Передачу объектов и массивов подробно разберём в уроке Списки, ключи и CSS Modules.
Пропсы только для чтения
Компонент не может менять свои пропсы. Данные идут сверху вниз — от родителя к ребёнку, как вода по трубам. Если ребёнку нужно что-то изменить, это делает родитель (как именно — узнаем в уроке про useState).
function Price({ value }) {
value = value * 2; // ❌ так делать нельзя: пропсы — только для чтения
return <span>{value}</span>;
}
function PriceWithTax({ value }) {
const withTax = value * 1.12; // ✅ создаём новую переменную
return <span>{withTax.toFixed(0)} ₸</span>;
}
Специальный проп children
Иногда в компонент хочется передать не просто данные, а целый кусок разметки — как в обычный тег <div>...</div>. Всё, что вы пишете между открывающим и закрывающим тегом компонента, попадает в проп children.
// components/Card.jsx
function Card({ title, children }) {
return (
<div style={{ border: '1px solid #ddd', borderRadius: 12, padding: 16 }}>
<h3>{title}</h3>
<div>{children}</div>
</div>
);
}
export default Card;
<Card title="О нас">
<p>Мы делаем лучшие сайты в городе.</p>
<button>Связаться</button>
</Card>
<Card title="Контакты">
<p>Телефон: +7 700 000 00 00</p>
</Card>
Card — это «рамка», а содержимое каждый раз своё. Так делают модальные окна, макеты страниц, секции.
Композиция: компоненты внутри компонентов
Большой интерфейс собирают из маленьких частей, передавая данные вниз по дереву:
function Avatar({ src, size = 64 }) {
return <img src={src} width={size} height={size} style={{ borderRadius: '50%' }} alt="" />;
}
function UserInfo({ name, role }) {
return (
<div>
<strong>{name}</strong>
<p>{role}</p>
</div>
);
}
function Comment({ author, avatar, role, text }) {
return (
<div style={{ display: 'flex', gap: 12 }}>
<Avatar src={avatar} size={48} />
<div>
<UserInfo name={author} role={role} />
<p>{text}</p>
</div>
</div>
);
}
Comment получает все данные и раздаёт нужные части детям: Avatar получает только картинку, UserInfo — имя и роль. Каждый маленький компонент легко переиспользовать в другом месте.
Ant Design: готовые компоненты
Верстать с нуля кнопки, модальные окна, таблицы, выпадающие списки — долго. Поэтому существуют UI-библиотеки — наборы готовых красивых компонентов. Одна из самых популярных — Ant Design (сокращённо antd). Она особенно хороша для админок и личных кабинетов: таблицы, формы, меню, уведомления.
Установка
npm install antd
Стили antd подключаются автоматически — отдельно импортировать CSS не нужно. Для иконок есть отдельный пакет:
npm install @ant-design/icons
Первые компоненты
import { Button, Space } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
function App() {
return (
<Space>
<Button type="primary" icon={<PlusOutlined />}>
Добавить
</Button>
<Button>Обычная</Button>
<Button type="dashed">Пунктирная</Button>
<Button danger icon={<DeleteOutlined />}>
Удалить
</Button>
<Button type="primary" loading>
Загрузка
</Button>
</Space>
);
}
export default App;
Разбор:
import { Button, Space } from 'antd'— компоненты antd импортируются именованным импортом.type="primary"— проп, который меняет вид кнопки. Видите? Компоненты antd настраиваются теми же пропсами, что мы только что изучили.icon={<PlusOutlined />}— в проп можно передать даже JSX-элемент.danger,loading— логические пропсы без значения (то же, чтоdanger={true}).Space— обёртка, которая ставит одинаковые отступы между детьми.
Карточка товара на antd
Соберём собственный компонент на основе компонентов antd:
// components/ProductCard.jsx
import { Card, Button, Tag, Typography } from 'antd';
import { ShoppingCartOutlined } from '@ant-design/icons';
const { Title, Text } = Typography;
function ProductCard({ title, price, image, isNew = false }) {
return (
<Card
hoverable
style={{ width: 260 }}
cover={<img src={image} alt={title} />}
>
{isNew && <Tag color="green">Новинка</Tag>}
<Title level={4}>{title}</Title>
<Text strong>{price.toLocaleString('ru-RU')} ₸</Text>
<div style={{ marginTop: 12 }}>
<Button type="primary" icon={<ShoppingCartOutlined />} block>
В корзину
</Button>
</div>
</Card>
);
}
export default ProductCard;
// App.jsx
import { Flex } from 'antd';
import ProductCard from './components/ProductCard';
function App() {
return (
<Flex gap={16} wrap style={{ padding: 24 }}>
<ProductCard title="Наушники" price={45000} image="https://placehold.co/260x180" isNew />
<ProductCard title="Клавиатура" price={32000} image="https://placehold.co/260x180" />
<ProductCard title="Мышь" price={12000} image="https://placehold.co/260x180" />
</Flex>
);
}
export default App;
Что нового:
Cardс пропомcover— картинка сверху,hoverable— тень при наведении.Typography— набор компонентов для текста;Title level={4}— этоh4.price.toLocaleString('ru-RU')превратит45000в45 000с пробелами.blockрастягивает кнопку на всю ширину.Flex— компонент-обёртка на основе flexbox:gap— отступ,wrap— перенос.
Своя тема через ConfigProvider
Хотите поменять основной цвет во всём приложении? Оберните приложение в ConfigProvider:
// main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { ConfigProvider } from 'antd';
import ruRU from 'antd/locale/ru_RU';
import App from './App';
createRoot(document.getElementById('root')).render(
<StrictMode>
<ConfigProvider
locale={ruRU}
theme={{ token: { colorPrimary: '#7c3aed', borderRadius: 10 } }}
>
<App />
</ConfigProvider>
</StrictMode>
);
theme.token.colorPrimary— главный цвет: всеprimary-кнопки станут фиолетовыми.locale={ruRU}— русские надписи во встроенных компонентах (например, в календаре или пагинации).
Все компоненты и их пропсы описаны в документации на ant.design — там у каждого компонента есть живые примеры, которые можно скопировать.
Типичные ошибки
- Путают default и named импорт — компонент оказывается
undefined, React падает с ошибкой «Element type is invalid». - Передают число строкой —
age="21"вместоage={21}. - Опечатка в имени пропса — передали
titel, а читаетеtitle. Ошибки не будет, просто ничего не выведется. Проверяйте черезconsole.log(props). - Пытаются изменить проп внутри компонента. Пропсы — только для чтения.
- Забывают импортировать компонент antd или иконку — ошибка «Button is not defined».
Практика
- Разложить по файлам. Вынесите
Header,FooterиMainиз прошлого урока в папкуsrc/components, каждый в свой файл сexport default. Ожидаемый результат:App.jsxсодержит только импорты и сборку страницы. - Карточка пользователя. Сделайте компонент
UserCardс пропсамиname,age,emailиavatar(по умолчанию — картинка-заглушка). Выведите вAppтри разные карточки. Ожидаемый результат: одна функция — три разных пользователя. - Обёртка
Section. Создайте компонентSectionс пропомtitleиchildren. Оберните им блоки «О нас», «Услуги», «Контакты». Ожидаемый результат: одинаковое оформление секций с разным содержимым. - Именованный экспорт. Создайте файл
Badges.jsxс компонентамиSuccessBadge,ErrorBadge,InfoBadge(каждый принимаетtext). Импортируйте все три одной строкой. - Ant Design. Установите
antdи@ant-design/icons. Сделайте страницу «Каталог» из 4–6ProductCardна компонентахCard,Tag,Button,Typography. Поменяйте основной цвет черезConfigProvider. Ожидаемый результат: сетка аккуратных карточек в цветах вашей темы.
Итоги
- Один компонент — один файл; папка
componentsдля переиспользуемых частей. export default— один на файл, импорт без скобок; именованныйexport— много на файл, импорт в{}точно по имени.- Пропсы — данные от родителя к ребёнку, передаются как атрибуты и приходят одним объектом.
- Пропсы деструктурируют в параметрах, можно задавать значения по умолчанию; менять их нельзя.
children— всё, что лежит между тегами компонента.- Ant Design даёт готовые компоненты (
Button,Card,Space,Flex,Typography), которые настраиваются пропсами, а тема задаётся черезConfigProvider.