К списку правил

Паттерн Compound Components

Назначение

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

Основные правила

  1. Следование общим правилам

  2. Особенности compound components

Структура файлов

Вариант 1: Все в одном файле

user-form/
├── user-form.form.tsx           # Основной файл с compound components
├── user-form.form.module.css    # Стили
└── user-form.form.test.ts       # Тесты

Вариант 2: С выделением сложных компонентов

user-form/
├── user-form.form.tsx           # Основной файл с compound components
├── user-form.form.module.css    # Стили
├── user-form.form.test.ts       # Тесты
└── components/                  # Опционально: сложные компоненты
    ├── fields/
    │   ├── user-fields.component.tsx
    │   └── user-fields.component.module.css
    └── actions/
        ├── form-actions.component.tsx
        └── form-actions.component.module.css

Пример реализации

// user-form.form.tsx
'use client';

import { UserFields } from './components/fields/user-fields.component';
import { FormActions } from './components/actions/form-actions.component';
import styles from './user-form.form.module.css';

// Общие типы для формы
type FormData = {
    name: string;
    email: string;
};

// Root компонент
function Root({
    onSubmit,
    children,
}: {
    onSubmit: (data: FormData) => void;
    children: React.ReactNode;
}) {
    return (
        <form className={styles.form} onSubmit={handleSubmit}>
            {children}
        </form>
    );
}

// Header компонент
function Header({ title }: { title: string }) {
    return <div className={styles.header}>{title}</div>;
}

// Footer компонент
function Footer({ children }: { children: React.ReactNode }) {
    return <div className={styles.footer}>{children}</div>;
}

// Экспорт через объект
export const UserForm = {
    Root,
    Header,
    Footer,
    Fields: UserFields,    // Импортированная имплементация
    Actions: FormActions,  // Импортированная имплементация
};

Имплементация дочерних компонентов

// components/fields/user-fields.component.tsx
import styles from './user-fields.component.module.css';

export function UserFields({
    values,
    onChange,
}: {
    values: FormData;
    onChange: (field: keyof FormData, value: string) => void;
}) {
    return (
        <div className={styles.fields}>
            <input
                value={values.name}
                onChange={(e) => onChange('name', e.target.value)}
            />
            <input
                value={values.email}
                onChange={(e) => onChange('email', e.target.value)}
            />
        </div>
    );
}

Использование

function UserManagement() {
  return (
    <UserForm.Root onSubmit={handleSubmit}>
      <UserForm.Header title="Создание пользователя" />
      <UserForm.Fields values={values} onChange={handleChange} />
      <UserForm.Footer>
        <UserForm.Actions onCancel={handleCancel} />
      </UserForm.Footer>
    </UserForm.Root>
  );
}

Преимущества подхода

  1. Организация кода

  2. Переиспользование

  3. Поддерживаемость

Рекомендации

  1. Именование

  2. Типизация

  3. Организация