Компоненты
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от shared/ui до screens.
Архитектурные слои и их назначение описаны в разделе Архитектура.
Правила организации
- Один компонент — один файл.
- Компонент не содержит бизнес-логики — логика и сайд-эффекты выносятся в хуки или сторы.
- Дочерние компоненты размещаются в сегменте
ui/и подчиняются тем же правилам структуры. - Публичный API модуля — только
index.ts. Прямые импорты внутренних файлов запрещены.
Базовая структура компонента
Минимальный набор файлов: компонент, стили, типы и публичный экспорт.
text
container/
├── styles/
│ └── container.module.css
├── types/
│ └── container.type.ts
├── container.tsx
└── index.tsИменования
- Имя корневого css класса всегда
.root - Тип пропсов именуется
{ComponentName}Props. - Тип пользовательских параметров именуется
{ComponentName}Params.
Типизация
Структура типов компонента показана в примере. Ниже — обоснования ключевых решений.
typeвместоinterface— гибче для пропсов: поддерживает union, intersection, mapped types. Declaration merging пропсам не нужно.- Без
FC— неявно добавляетchildren, усложняет дженерики, не даёт преимуществ перед аннотацией параметра. - Типы в
types/, а не в.tsx— предотвращает циклические зависимости (компонент импортирует хук, хук импортирует тип из компонента) и разделяет ответственность:.tsxдля рендера,.type.tsдля данных. - Без возвращаемого типа — TypeScript выводит из JSX. Осознанное исключение из базового правила.
Реализация
- Пропсы деструктурируются в теле компонента, не в параметрах.
- Порядок: пользовательские → системные (
children,className) →...htmlAttr. classNameобъединяется с корневым классом черезcl():cl(styles.root, className)....htmlAttrпрокидывается на корневой элемент.
Пример
container/types/container.type.ts
ts
import type { HTMLAttributes } from 'react'
/**
* Параметры компонента Container.
*/
export type ContainerParams = {}
/** HTML-атрибуты корневого элемента. */
type RootAttrs = HTMLAttributes<HTMLDivElement>
export type ContainerProps = RootAttrs & ContainerParamscontainer/styles/container.module.css
css
.root {
max-width: var(--content-width);
margin: 0 auto;
padding: 0 var(--spacing-4);
}container/container.tsx
tsx
import cl from 'clsx'
import type { ContainerProps } from './types/container.type'
import styles from './styles/container.module.css'
/**
* Контейнер с адаптивной максимальной шириной.
*
* Используется для:
* - обёртки контента страниц с ограничением ширины
* - центрирования блоков в лейауте
*/
export const Container = (props: ContainerProps) => {
const { children, className, ...htmlAttr } = props
return (
<div {...htmlAttr} className={cl(styles.root, className)}>
{children}
</div>
)
}container/index.ts
ts
export { Container } from './container'