Компонент в React — это функция, которая принимает данные (props) и возвращает описание UI (JSX-элемент). Компоненты — строительные блоки любого React-приложения.
// Простейший компонент — просто функция!
function Greeting() {
return <h1>Привет, мир!</h1>
}
// Использование как тег:
const app = <Greeting />React рекомендует функциональные компоненты (Function Components). До 2019 года использовались классовые компоненты, но с появлением хуков (hooks) функциональные компоненты умеют всё то же самое и стали стандартом:
// Современный функциональный компонент
function UserCard({ name, email, avatar }) {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
</div>
)
}Компоненты должны начинаться с заглавной буквы (PascalCase). React использует это для различия между HTML-тегами и компонентами:
<div> // HTML-тег (строчная) — React создаёт обычный DOM-элемент
<Div> // Компонент (заглавная) — React вызывает функцию Div()
<Button> // Компонент Button
<button> // HTML-кнопкаГлавная сила компонентов — композиция. Компоненты собираются в дерево:
// Атомарные компоненты (нижний уровень)
function Avatar({ src, alt }) {
return <img className="avatar" src={src} alt={alt} />
}
function Button({ children, onClick }) {
return <button onClick={onClick}>{children}</button>
}
// Составной компонент
function UserCard({ user, onFollow }) {
return (
<div className="card">
<Avatar src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<Button onClick={onFollow}>Подписаться</Button>
</div>
)
}
// Компонент-контейнер (верхний уровень)
function UserList({ users }) {
return (
<div className="user-list">
{users.map(user => (
<UserCard key={user.id} user={user} onFollow={() => {}} />
))}
</div>
)
}
// Корневой компонент
function App() {
return (
<main>
<h1>Пользователи</h1>
<UserList users={[]} />
</main>
)
}Каждый компонент живёт в своём файле (одна ответственность):
// Button.tsx — именованный экспорт
export function Button({ children, variant = 'primary' }) {
return (
<button className={`btn btn-${variant}`}>
{children}
</button>
)
}
// App.tsx — экспорт по умолчанию
export default function App() {
return <Button variant="secondary">Нажми меня</Button>
}В React нет жёсткого правила: default vs named export — ваш выбор. Однако named exports лучше работают с автоимпортом в IDE.
Компонент может вернуть:
null (ничего не рендерить)<></>function StatusBadge({ status }) {
if (status === 'hidden') return null // ничего не рендерим
return (
<span className={`badge badge-${status}`}>
{status === 'active' ? 'Активен' : 'Неактивен'}
</span>
)
}Вы знакомы с Vue SFC. Сравнение:
<!-- Vue: UserCard.vue -->
<template>
<div class="card">
<h2>{{ user.name }}</h2>
<button @click="$emit('follow')">Подписаться</button>
</div>
</template>
<script setup>
defineProps(['user'])
defineEmits(['follow'])
</script>// React: UserCard.jsx
function UserCard({ user, onFollow }) {
return (
<div className="card">
<h2>{user.name}</h2>
<button onClick={onFollow}>Подписаться</button>
</div>
)
}В React всё в одном файле, нет разделения template/script/style. Стили обычно выносят в CSS-модули, Tailwind или CSS-in-JS библиотеки.
Компонентная система с нуля на чистом JavaScript: функции-компоненты, дерево компонентов и рендеринг
// Реализуем компонентную систему на vanilla JS
// чтобы понять как React организует компоненты под капотом
// ============================================================
// Базовая инфраструктура: рендеринг компонентов
// ============================================================
// Компонент — функция, возвращающая HTML-строку (упрощение)
// В React функция возвращает JSX (объект), мы — строку
// Атомарный компонент: кнопка
function Button({ label, variant = 'primary' }) {
// Аналог React:
// function Button({ label, variant = 'primary' }) {
// return <button className={`btn btn-${variant}`}>{label}</button>
// }
return `<button class="btn btn-${variant}">${label}</button>`
}
// Атомарный компонент: аватар
function Avatar({ src, name }) {
// React: return <img src={src} alt={name} className="avatar" />
return `<img src="${src}" alt="${name}" class="avatar" />`
}
// ============================================================
// Составные компоненты — используют другие компоненты
// ============================================================
// Карточка пользователя — составной компонент
function UserCard({ user, onFollow }) {
// React:
// function UserCard({ user, onFollow }) {
// return (
// <div className="card">
// <Avatar src={user.avatar} name={user.name} />
// <div className="card-body">
// <h2>{user.name}</h2>
// <p>{user.bio}</p>
// <Button label="Подписаться" variant="primary" />
// </div>
// </div>
// )
// }
const avatar = Avatar({ src: user.avatar, name: user.name })
const followBtn = Button({ label: 'Подписаться', variant: 'primary' })
return `
<div class="card">
${avatar}
<div class="card-body">
<h2>${user.name}</h2>
<p>${user.bio}</p>
${followBtn}
</div>
</div>
`
}
// Список пользователей — компонент-контейнер
function UserList({ users }) {
// React: users.map(user => <UserCard key={user.id} user={user} />)
const cards = users
.map(user => UserCard({ user, onFollow: () => {} }))
.join('')
return `<div class="user-list">${cards}</div>`
}
// Корневой компонент — точка входа
function App({ users }) {
// React: function App() { return <main><h1>Команда</h1><UserList /></main> }
const list = UserList({ users })
return `<main><h1>Команда</h1>${list}</main>`
}
// ============================================================
// Рендеринг дерева компонентов
// ============================================================
const teamData = [
{ id: 1, name: 'Алексей Иванов', bio: 'Frontend разработчик', avatar: '/img/alex.jpg' },
{ id: 2, name: 'Мария Петрова', bio: 'UI/UX дизайнер', avatar: '/img/maria.jpg' },
{ id: 3, name: 'Дмитрий Сидоров', bio: 'Backend разработчик', avatar: '/img/dmitry.jpg' },
]
const html = App({ users: teamData })
console.log('Дерево компонентов App > UserList > UserCard > (Avatar, Button)')
console.log('Сгенерированный HTML (первые 200 символов):')
console.log(html.trim().slice(0, 200) + '...')
console.log('')
console.log('В React компоненты возвращают объекты (JSX), а не строки.')
console.log('React сам строит DOM из этих объектов через ReactDOM.render()')Компонент в React — это функция, которая принимает данные (props) и возвращает описание UI (JSX-элемент). Компоненты — строительные блоки любого React-приложения.
// Простейший компонент — просто функция!
function Greeting() {
return <h1>Привет, мир!</h1>
}
// Использование как тег:
const app = <Greeting />React рекомендует функциональные компоненты (Function Components). До 2019 года использовались классовые компоненты, но с появлением хуков (hooks) функциональные компоненты умеют всё то же самое и стали стандартом:
// Современный функциональный компонент
function UserCard({ name, email, avatar }) {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
</div>
)
}Компоненты должны начинаться с заглавной буквы (PascalCase). React использует это для различия между HTML-тегами и компонентами:
<div> // HTML-тег (строчная) — React создаёт обычный DOM-элемент
<Div> // Компонент (заглавная) — React вызывает функцию Div()
<Button> // Компонент Button
<button> // HTML-кнопкаГлавная сила компонентов — композиция. Компоненты собираются в дерево:
// Атомарные компоненты (нижний уровень)
function Avatar({ src, alt }) {
return <img className="avatar" src={src} alt={alt} />
}
function Button({ children, onClick }) {
return <button onClick={onClick}>{children}</button>
}
// Составной компонент
function UserCard({ user, onFollow }) {
return (
<div className="card">
<Avatar src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<Button onClick={onFollow}>Подписаться</Button>
</div>
)
}
// Компонент-контейнер (верхний уровень)
function UserList({ users }) {
return (
<div className="user-list">
{users.map(user => (
<UserCard key={user.id} user={user} onFollow={() => {}} />
))}
</div>
)
}
// Корневой компонент
function App() {
return (
<main>
<h1>Пользователи</h1>
<UserList users={[]} />
</main>
)
}Каждый компонент живёт в своём файле (одна ответственность):
// Button.tsx — именованный экспорт
export function Button({ children, variant = 'primary' }) {
return (
<button className={`btn btn-${variant}`}>
{children}
</button>
)
}
// App.tsx — экспорт по умолчанию
export default function App() {
return <Button variant="secondary">Нажми меня</Button>
}В React нет жёсткого правила: default vs named export — ваш выбор. Однако named exports лучше работают с автоимпортом в IDE.
Компонент может вернуть:
null (ничего не рендерить)<></>function StatusBadge({ status }) {
if (status === 'hidden') return null // ничего не рендерим
return (
<span className={`badge badge-${status}`}>
{status === 'active' ? 'Активен' : 'Неактивен'}
</span>
)
}Вы знакомы с Vue SFC. Сравнение:
<!-- Vue: UserCard.vue -->
<template>
<div class="card">
<h2>{{ user.name }}</h2>
<button @click="$emit('follow')">Подписаться</button>
</div>
</template>
<script setup>
defineProps(['user'])
defineEmits(['follow'])
</script>// React: UserCard.jsx
function UserCard({ user, onFollow }) {
return (
<div className="card">
<h2>{user.name}</h2>
<button onClick={onFollow}>Подписаться</button>
</div>
)
}В React всё в одном файле, нет разделения template/script/style. Стили обычно выносят в CSS-модули, Tailwind или CSS-in-JS библиотеки.
Компонентная система с нуля на чистом JavaScript: функции-компоненты, дерево компонентов и рендеринг
// Реализуем компонентную систему на vanilla JS
// чтобы понять как React организует компоненты под капотом
// ============================================================
// Базовая инфраструктура: рендеринг компонентов
// ============================================================
// Компонент — функция, возвращающая HTML-строку (упрощение)
// В React функция возвращает JSX (объект), мы — строку
// Атомарный компонент: кнопка
function Button({ label, variant = 'primary' }) {
// Аналог React:
// function Button({ label, variant = 'primary' }) {
// return <button className={`btn btn-${variant}`}>{label}</button>
// }
return `<button class="btn btn-${variant}">${label}</button>`
}
// Атомарный компонент: аватар
function Avatar({ src, name }) {
// React: return <img src={src} alt={name} className="avatar" />
return `<img src="${src}" alt="${name}" class="avatar" />`
}
// ============================================================
// Составные компоненты — используют другие компоненты
// ============================================================
// Карточка пользователя — составной компонент
function UserCard({ user, onFollow }) {
// React:
// function UserCard({ user, onFollow }) {
// return (
// <div className="card">
// <Avatar src={user.avatar} name={user.name} />
// <div className="card-body">
// <h2>{user.name}</h2>
// <p>{user.bio}</p>
// <Button label="Подписаться" variant="primary" />
// </div>
// </div>
// )
// }
const avatar = Avatar({ src: user.avatar, name: user.name })
const followBtn = Button({ label: 'Подписаться', variant: 'primary' })
return `
<div class="card">
${avatar}
<div class="card-body">
<h2>${user.name}</h2>
<p>${user.bio}</p>
${followBtn}
</div>
</div>
`
}
// Список пользователей — компонент-контейнер
function UserList({ users }) {
// React: users.map(user => <UserCard key={user.id} user={user} />)
const cards = users
.map(user => UserCard({ user, onFollow: () => {} }))
.join('')
return `<div class="user-list">${cards}</div>`
}
// Корневой компонент — точка входа
function App({ users }) {
// React: function App() { return <main><h1>Команда</h1><UserList /></main> }
const list = UserList({ users })
return `<main><h1>Команда</h1>${list}</main>`
}
// ============================================================
// Рендеринг дерева компонентов
// ============================================================
const teamData = [
{ id: 1, name: 'Алексей Иванов', bio: 'Frontend разработчик', avatar: '/img/alex.jpg' },
{ id: 2, name: 'Мария Петрова', bio: 'UI/UX дизайнер', avatar: '/img/maria.jpg' },
{ id: 3, name: 'Дмитрий Сидоров', bio: 'Backend разработчик', avatar: '/img/dmitry.jpg' },
]
const html = App({ users: teamData })
console.log('Дерево компонентов App > UserList > UserCard > (Avatar, Button)')
console.log('Сгенерированный HTML (первые 200 символов):')
console.log(html.trim().slice(0, 200) + '...')
console.log('')
console.log('В React компоненты возвращают объекты (JSX), а не строки.')
console.log('React сам строит DOM из этих объектов через ReactDOM.render()')Создай три компонента и скомпонуй их в App. Компонент Avatar принимает пропсы src и name и рендерит img с круглым стилем. Компонент UserCard принимает name, role и avatarSrc и рендерит карточку с Avatar внутри. Компонент App рендерит заголовок и два UserCard с разными данными.
В UserCard используй <Avatar src={avatarSrc} name={name} /> — название компонента с заглавной буквы. В App используй <UserCard name="..." role="..." avatarSrc="..." /> для каждого сотрудника. Компонент — это функция, возвращающая JSX.