← React/Компоненты React#258 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: React или VueПрактика: React setТермин: React HooksТема: React: хуки и экосистема

Компоненты React

Что такое компонент

Компонент в 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 обязательно

Компоненты должны начинаться с заглавной буквы (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.

Возвращаемое значение

Компонент может вернуть:

  • JSX-элемент (самый частый случай)
  • Массив элементов
  • Строку или число
  • null (ничего не рендерить)
  • Fragment <></>
  • function StatusBadge({ status }) {
      if (status === 'hidden') return null  // ничего не рендерим
    
      return (
        <span className={`badge badge-${status}`}>
          {status === 'active' ? 'Активен' : 'Неактивен'}
        </span>
      )
    }

    Компонент vs Vue

    Вы знакомы с 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

    Что такое компонент

    Компонент в 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 обязательно

    Компоненты должны начинаться с заглавной буквы (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.

    Возвращаемое значение

    Компонент может вернуть:

  • JSX-элемент (самый частый случай)
  • Массив элементов
  • Строку или число
  • null (ничего не рендерить)
  • Fragment <></>
  • function StatusBadge({ status }) {
      if (status === 'hidden') return null  // ничего не рендерим
    
      return (
        <span className={`badge badge-${status}`}>
          {status === 'active' ? 'Активен' : 'Неактивен'}
        </span>
      )
    }

    Компонент vs Vue

    Вы знакомы с 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.

    Загружаем среду выполнения...
    Загружаем AI-помощника...