В чем разница между PeerComponent и Component?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
В чем разница между различными типами компонентов
Введение
В фронтенд-разработке существуют разные типы компонентов в зависимости от архитектуры приложения. Предположу, что вопрос касается разницы между компонентами общего пользования и специализированными компонентами.
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>;
}
Итоговые рекомендации
- Используй кастомные хуки — современный способ разделения логики
- Разделяй контейнеры и представления — улучшает тестируемость и переиспользование
- Создавай переиспользуемые компоненты — используй при наличии flexible props
- Избегай глубокой вложенности — используй composition вместо наследования
- Следуй SOLID принципам — особенно Single Responsibility Principle