Компонент: что это такое

Компонент — изолированная, переиспользуемая единица UI: функция (или класс), принимающая props и возвращающая разметку (JSX/HTML) с опциональным поведением и состоянием.

Зачем нужно

Компонентный подход — основа современного фронтенда. Вместо монолитного HTML, где изменение кнопки требует правки в десятках мест, компонент описывает кнопку один раз — и используется везде. Изменение компонента мгновенно применяется всюду. Это даёт переиспользование, изоляцию (баг в одном компоненте не ломает другие) и понятную структуру приложения.

Где используется

  • React, Vue, Angular, Svelte — все современные фреймворки строятся на компонентах
  • Web Components (нативный стандарт браузера) — custom elements
  • React Native — компоненты для мобильных приложений
  • Любой UI: от кнопки до целой страницы — всё компонент

Функциональный компонент (React)

// Минимальный компонент — функция, возвращающая JSX
function Greeting({ name }) {
  return <h1>Привет, {name}!</h1>;
}

// Использование как HTML-элемент
<Greeting name="Мир" />

Компонент с состоянием и эффектами

import { useState, useEffect } from 'react';

// Компонент инкапсулирует: UI + состояние + поведение
function ProductCard({ productId, onAddToCart }) {
  // Состояние — личные данные компонента
  const [product, setProduct] = useState(null);
  const [isWishlisted, setIsWishlisted] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  // Эффект — побочное действие после рендера
  useEffect(() => {
    fetch(`/api/products/${productId}`)
      .then(r => r.json())
      .then(data => {
        setProduct(data);
        setIsLoading(false);
      });
  }, [productId]);

  const handleWishlist = () => setIsWishlisted(prev => !prev);

  if (isLoading) return <div className="card-skeleton" />;

  // JSX — декларативное описание UI
  return (
    <article className="product-card">
      <img src={product.imageUrl} alt={product.name} />
      <h2>{product.name}</h2>
      <p className="price">{product.price} ₽</p>
      <div className="actions">
        <button onClick={ => onAddToCart(product)}>В корзину</button>
        <button
          onClick={handleWishlist}
          aria-pressed={isWishlisted}
          className={isWishlisted ? 'wishlisted' : ''}
        >
          {isWishlisted ? 'Убрать из избранного' : 'В избранное'}
        </button>
      </div>
    </article>
  );
}

Композиция компонентов

// Компоненты собираются в дерево — как матрёшки
function App() {
  return (
    <Layout>           {/* Layout содержит Header + main + Footer */}
      <Header />
      <main>
        <ProductGrid>  {/* Grid рендерит массив ProductCard */}
          {products.map(p => (
            <ProductCard
              key={p.id}
              productId={p.id}
              onAddToCart={addToCart}
            />
          ))}
        </ProductGrid>
      </main>
      <Footer />
    </Layout>
  );
}

Принципы хорошего компонента

Единственная ответственность — один компонент, одна задача
Переиспользование — работает в разных контекстах
Изоляция — не зависит от глобального состояния без необходимости
Тестируемость — легко протестировать в изоляции
Небольшой размер — до ~150 строк; больше → разбить

Частые ошибки

  • Компоненты-монолиты — 500+ строк с логикой, стилями, данными; разбивайте на более мелкие.
  • Прямая мутация пропсовprops.items.push(...) нарушает однонаправленный поток; props иммутабельны.
  • Логика рендеринга в коллбэкахonClick={ => { if (condition) setA(1); else setB(2); /* ещё 20 строк */ }} — выносите логику в именованные функции.
  • Компонент знает слишком много — обращается к глобальному store напрямую вместо получения данных через props.

Связанные темы

Ресурсы