MUI (Material-UI) предоставляет мощный механизм для создания generic компонентов, который позволяет компоновать и переиспользовать элементы интерфейса с типизацией и высокой гибкостью. Generic компоненты особенно полезны в TypeScript, но принципы применимы и в чистом JavaScript через пропсы и контракты компонентов.
Generic компоненты в MUI строятся вокруг типизации пропсов и контроля над внутренними элементами, что позволяет создавать настраиваемые элементы с сохранением всех стилей и функциональности.
componentВ MUI почти все компоненты поддерживают проп component,
который позволяет изменять HTML-элемент или другой React-компонент,
используемый внутри:
import Button from '@mui/material/Button';
import { Link } from 'react-router-dom';
<Button component={Link} to="/home">
Перейти на главную
</Button>
component может быть:
'div', 'span',
'a')Сохраняется совместимость с MUI API, включая стили, обработку событий и поведение.
Ключевой момент: использование
componentпревращает компонент в generic — он может рендерить любой элемент без потери функциональности MUI.
TypeScript позволяет задавать тип компонента через дженерик, сохраняя строгую типизацию пропсов:
import Box, { BoxProps } from '@mui/material/Box';
interface CustomBoxProps<T extends React.ElementType> {
component?: T;
label: string;
}
function CustomBox<T extends React.ElementType = 'div'>({
component,
label,
...props
}: CustomBoxProps<T> & Omit<React.ComponentProps<T>, keyof CustomBoxProps<T>>) {
const Component = component || 'div';
return <Component {...props}>{label}</Component>;
}
// Использование
<CustomBox component="section" label="Контент в секции" />
<CustomBox component="button" label="Нажми меня" onCl ick={() => alert('Clicked')} />
T extends React.ElementType позволяет
передавать любой элемент или компонент.Omit<React.ComponentProps<T>, keyof CustomBoxProps<T>>
исключает дублирование пропсов, предотвращая конфликты.Это делает компонент максимально универсальным и переиспользуемым, сохраняя поддержку событий и атрибутов целевого HTML-элемента.
MUI предоставляет встроенные generic компоненты:
Box
Универсальный контейнер, поддерживающий все системные пропсы
(sx, display, margin и
др.)
Может рендерить любой элемент через component:
<Box component="section" sx={{ p: 2, bgcolor: 'primary.main' }}>
Секция
</Box>Typography
Позволяет менять HTML-элемент через component,
сохраняя стили текста:
<Typography variant="h6" component="h2">
Заголовок второго уровня
</Typography>Button
Может рендерить ссылку или другой компонент, сохраняя стили Material Design:
<Button component="a" href="#link">
Ссылка-кнопка
</Button>Generic подход полезен для создания компонентов с настраиваемой семантикой и стилизацией:
import { styled } from '@mui/material/styles';
import Paper, { PaperProps } from '@mui/material/Paper';
interface CardProps<T extends React.ElementType> {
component?: T;
title: string;
content: string;
}
const StyledPaper = styled(Paper)(({ theme }) => ({
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius,
}));
function Card<T extends React.ElementType = 'div'>({
component,
title,
content,
...props
}: CardProps<T> & Omit<React.ComponentProps<T>, keyof CardProps<T>>) {
const Component = component || 'div';
return (
<StyledPaper component={Component} {...props}>
<h3>{title}</h3>
<p>{content}</p>
</StyledPaper>
);
}
// Пример использования
<Card component="article" title="Статья" content="Содержимое статьи" />
<Card component="section" title="Секция" content="Содержимое секции" />
styled, что обеспечивает
повторное использование и единый стиль.component позволяет рендерить любую семантическую
обертку (div, section, article,
button).Generic компоненты должны корректно обрабатывать события и дополнительные пропсы целевого элемента:
<Card
component="button"
title="Кнопка"
content="Нажми меня"
onCl ick={() => console.log('Clicked')}
/>
onClick доступен, если
component — это кнопка.Такой подход обеспечивает полную совместимость с MUI и React, позволяя использовать стандартные DOM-события без потери типизации.
Generic компоненты отлично сочетаются с sx и системными
пропсами:
<Card
component="section"
title="Секция"
content="Контент секции"
sx={{ bgcolor: 'secondary.light', border: 1, borderColor: 'secondary.main' }}
/>
sx работает независимо от того, какой компонент
используется внутри component.component для смены HTML-тега или
React-компонента.T extends React.ElementType).Использование generic компонентов в MUI открывает возможности для создания универсальных библиотек UI-элементов, которые легко адаптируются под разные семантические требования и визуальный стиль.