Композиция и переиспользование
React построен на композиции: мелкие компоненты собираются в крупные. Это делает код переиспользуемым и поддерживаемым.
Композиция через children
function Card({ children }: { children: React.ReactNode }) {
return <div className="card">{children}</div>;
}
<Card>
<h2>Заголовок</h2>
<p>Текст</p>
</Card>
Именованные слоты
Если нужно несколько областей — передавай пропсы с JSX:
type LayoutProps = {
header: React.ReactNode;
sidebar: React.ReactNode;
children: React.ReactNode;
};
function Layout({ header, sidebar, children }: LayoutProps) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
<Layout
header={<h1>Мой сайт</h1>}
sidebar={<nav>...</nav>}
>
<p>Основной контент</p>
</Layout>
Специализация
Один базовый компонент и несколько специализированных:
function Button({ variant, children, ...props }: ButtonProps) {
return (
<button className={`btn btn-${variant}`} {...props}>
{children}
</button>
);
}
function PrimaryButton(props: Omit<ButtonProps, 'variant'>) {
return <Button variant="primary" {...props} />;
}
function DangerButton(props: Omit<ButtonProps, 'variant'>) {
return <Button variant="danger" {...props} />;
}
Omit<ButtonProps, 'variant'> исключает пропс variant из типа.
Spread пропсов
{...props} передаёт все пропсы дальше. Полезно для обёрток:
function Input(props: React.InputHTMLAttributes<HTMLInputElement>) {
return <input className="input" {...props} />;
}
<Input type="email" placeholder="Email" />
Композиция функций
Логику выносят в кастомные хуки:
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
function toggle() {
setValue((v) => !v);
}
return { value, toggle };
}
function Modal() {
const { value: isOpen, toggle } = useToggle();
return (
<>
<button onClick={toggle}>Открыть</button>
{isOpen && <div className="modal">...</div>}
</>
);
}
Render props
Компонент принимает функцию, которая возвращает разметку:
type ListProps<T> = {
items: T[];
renderItem: (item: T) => React.ReactNode;
};
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
<List
items={users}
renderItem={(user) => <li key={user.id}>{user.name}</li>}
/>
Используется реже, чем хуки, но иногда удобен.
Композиция вместо наследования
В React нет наследования компонентов. Вся переиспользуемость — через композицию, пропсы и хуки.
// Плохо: попытка наследования
class AdminButton extends Button {}
// Хорошо: специализация через пропсы
function AdminButton(props) {
return <Button variant="admin" {...props} />;
}
Итоги
- Композиция — основной способ переиспользования
children— для вложенного контента- Именованные слоты — через пропсы с JSX
- Spread пропсов — для обёрток
- Логика — в кастомных хуках