← Назад к вопросам

В чем разница между PeerComponent и Component?

2.0 Middle🔥 181 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чем разница между различными типами компонентов

Введение

В фронтенд-разработке существуют разные типы компонентов в зависимости от архитектуры приложения. Предположу, что вопрос касается разницы между компонентами общего пользования и специализированными компонентами.

1. Переиспользуемые компоненты (Reusable Components)

Переиспользуемые компоненты — это универсальные компоненты, которые можно использовать в разных местах приложения:

// Button.tsx — переиспользуемый компонент
interface ButtonProps {
  variant?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  onClick?: () => void;
  children: React.ReactNode;
}

export function Button({
  variant = 'primary',
  size = 'medium',
  disabled = false,
  onClick,
  children
}: ButtonProps) {
  return (
    <button
      className={`button button--${variant} button--${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

Характеристики:

  • Не зависит от других компонентов
  • Имеет гибкие props для настройки
  • Может использоваться в разных контекстах
  • Не содержит специфичную для одной страницы логику

2. Контейнерные компоненты (Container Components)

Контейнерные компоненты — это компоненты, которые управляют состоянием и логикой, часто содержат несколько дочерних компонентов:

// UserListContainer.tsx — контейнерный компонент
import { useEffect, useState } from 'react';
import { UserList } from './UserList';
import { useUsers } from '@/hooks/useUsers';

export function UserListContainer() {
  const { users, loading, error, fetchUsers } = useUsers();
  const [searchTerm, setSearchTerm] = useState('');

  useEffect(() => {
    fetchUsers();
  }, []);

  const filteredUsers = users.filter(u =>
    u.name.toLowerCase().includes(searchTerm.toLowerCase())
  );

  return (
    <div>
      <input
        type="text"
        placeholder="Search users"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      <UserList
        users={filteredUsers}
        loading={loading}
        error={error}
      />
    </div>
  );
}

Характеристики:

  • Управляет состоянием и логикой
  • Получает данные из API или хуков
  • Передает данные в представляющие компоненты
  • Содержит бизнес-логику

3. Представляющие компоненты (Presentational Components)

Представляющие компоненты — это компоненты, которые только отображают данные, полученные через props:

// UserList.tsx — представляющий компонент
interface User {
  id: string;
  name: string;
  email: string;
}

interface UserListProps {
  users: User[];
  loading: boolean;
  error: string | null;
}

export function UserList({ users, loading, error }: UserListProps) {
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          <strong>{user.name}</strong> - {user.email}
        </li>
      ))}
    </ul>
  );
}

Характеристики:

  • Не управляет состоянием (или минимально)
  • Получает данные через props
  • Фокусируется на отображении
  • Легко тестировать

4. Разница: Контейнер vs Представление

АспектКонтейнерПредставление
СостояниеУправляет состояниемНе управляет
PropsМинимум propsМного props
ДанныеПолучает данныеПолучает данные из props
ЛогикаСодержит логикуМинимум логики
ТестированиеСложнееЛегче
ПереиспользованиеСложнееЛегче

5. Пример с разделением ответственности

// ❌ Плохо — всё в одном компоненте
function UserListBad() {
  const [users, setUsers] = useState([]);
  const [searchTerm, setSearchTerm] = useState('');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    fetch('/api/users')
      .then(r => r.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  const filtered = users.filter(u =>
    u.name.includes(searchTerm)
  );

  return (
    <div>
      <input
        value={searchTerm}
        onChange={e => setSearchTerm(e.target.value)}
      />
      {loading && <div>Loading...</div>}
      <ul>
        {filtered.map(u => <li key={u.id}>{u.name}</li>)}
      </ul>
    </div>
  );
}

// ✅ Хорошо — разделение ответственности
// Контейнер
function UserListContainer() {
  const { users, loading } = useUsers();
  const [searchTerm, setSearchTerm] = useState('');
  const filtered = users.filter(u =>
    u.name.includes(searchTerm)
  );

  return (
    <UserListPresentation
      users={filtered}
      loading={loading}
      searchTerm={searchTerm}
      onSearchChange={setSearchTerm}
    />
  );
}

// Представление
function UserListPresentation({
  users,
  loading,
  searchTerm,
  onSearchChange
}: any) {
  return (
    <div>
      <input
        value={searchTerm}
        onChange={e => onSearchChange(e.target.value)}
      />
      {loading && <div>Loading...</div>}
      <ul>
        {users.map(u => <li key={u.id}>{u.name}</li>)}
      </ul>
    </div>
  );
}

6. Компоненты высшего порядка (HOC)

HOC — это функция, которая принимает компонент и возвращает новый компонент с дополнительной функциональностью:

// withAuth.tsx
function withAuth<P extends object>(
  Component: React.ComponentType<P>
): React.ComponentType<P> {
  return function ProtectedComponent(props: P) {
    const { isAuthenticated } = useAuth();

    if (!isAuthenticated) {
      return <div>Not authenticated</div>;
    }

    return <Component {...props} />;
  };
}

// Использование
function Dashboard(props: any) {
  return <div>Dashboard content</div>;
}

export const ProtectedDashboard = withAuth(Dashboard);

7. Render Props паттерн

Альтернатива HOC для повторного использования логики:

interface MouseTrackerProps {
  children: (position: { x: number; y: number }) => React.ReactNode;
}

function MouseTracker({ children }: MouseTrackerProps) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  return <>{children(position)}</>;
}

// Использование
<MouseTracker>
  {(pos) => <div>Mouse: {pos.x}, {pos.y}</div>}
</MouseTracker>

8. Кастомные хуки (современный подход)

Современный способ разделения логики:

// useMousePosition.ts
function useMousePosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  return position;
}

// Использование в компоненте
function MouseDisplay() {
  const position = useMousePosition();
  return <div>Mouse: {position.x}, {position.y}</div>;
}

Итоговые рекомендации

  1. Используй кастомные хуки — современный способ разделения логики
  2. Разделяй контейнеры и представления — улучшает тестируемость и переиспользование
  3. Создавай переиспользуемые компоненты — используй при наличии flexible props
  4. Избегай глубокой вложенности — используй composition вместо наследования
  5. Следуй SOLID принципам — особенно Single Responsibility Principle
В чем разница между PeerComponent и Component? | PrepBro