Перейти к основному содержимому

Быстрый старт

Создайте простую контактную форму за 5 минут.

Шаг 1: Создание компонентов для полей

ReFormer использует ваши собственные компоненты для отображения полей.

Базовые компоненты

src/components/ui/Input.tsx
import * as React from 'react';

interface InputProps {
value?: string;
onChange?: (value: string) => void;
onBlur?: () => void;
placeholder?: string;
disabled?: boolean;
}

export const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ value, onChange, onBlur, placeholder, disabled }, ref) => (
<input
ref={ref}
type="text"
value={value ?? ''}
onChange={(e) => onChange?.(e.target.value)}
onBlur={onBlur}
placeholder={placeholder}
disabled={disabled}
className="border rounded px-3 py-2 w-full"
/>
)
);
src/components/ui/Textarea.tsx
import * as React from 'react';

interface TextareaProps {
value?: string;
onChange?: (value: string) => void;
onBlur?: () => void;
placeholder?: string;
disabled?: boolean;
}

export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ value, onChange, onBlur, placeholder, disabled }, ref) => (
<textarea
ref={ref}
value={value ?? ''}
onChange={(e) => onChange?.(e.target.value)}
onBlur={onBlur}
placeholder={placeholder}
disabled={disabled}
rows={4}
className="border rounded px-3 py-2 w-full"
/>
)
);

Универсальный компонент FormField

Зачем FormField?

FormField автоматически:

  • Отображает label из componentProps
  • Связывает значение с формой
  • Вызывает markAsTouched() при blur
  • Показывает ошибки валидации

Данный компонент нужен не только для того что бы унифицировать работу с полями формы но и оптимизировать рендер формы.

src/components/ui/FormField.tsx
import * as React from 'react';
import { useFormControl, type FieldNode } from '@reformer/core';

interface FormFieldProps {
control: FieldNode<any>;
className?: string;
}

export const FormField: React.FC<FormFieldProps> = ({ control, className }) => {
const { value, errors, disabled, shouldShowError, componentProps } = useFormControl(control);

const Component = control.component;

return (
<div className={className}>
{componentProps.label && (
<label className="block mb-1 text-sm font-medium">{componentProps.label}</label>
)}

<Component
{...componentProps}
value={value ?? ''}
onChange={(e: unknown) => {
const newValue = (e as { target?: { value?: unknown } })?.target?.value ?? e;
control.setValue(newValue);
}}
onBlur={() => control.markAsTouched()}
disabled={disabled}
/>

{shouldShowError && (
<span className="text-red-500 text-sm mt-1 block">{errors[0]?.message}</span>
)}
</div>
);
};

Шаг 2: Описание интерфейса формы

src/forms/contact-form.type.ts
type ContactFormType = {
name: string;
email: string;
message: string;
};

Шаг 3: Создание описания формы

Ключевые моменты
  • createForm<T> — фабричная функция с автоматической типизацией
  • component — React-компонент для отображения поля
  • componentProps — пропсы для компонента (label, placeholder и т.д.)
  • validation — декларативная схема валидации
src/forms/contact-form.ts
import { createForm } from '@reformer/core';
import { required, email, minLength } from '@reformer/core/validators';
import { Input } from '@/components/ui/Input';
import { Textarea } from '@/components/ui/Textarea';
import { ContactFormType } from '@/forms/contact-form.type';

export const createContactForm = () =>
createForm<ContactFormType>({
form: {
name: { value: '', component: Input, componentProps: { label: 'Имя' } },
email: { value: '', component: Input, componentProps: { label: 'Email' } },
message: { value: '', component: Textarea, componentProps: { label: 'Сообщение' } },
},
validation: (path) => {
required(path.name);
minLength(path.name, 2);
required(path.email);
email(path.email);
required(path.message);
minLength(path.message, 10);
},
});

Шаг 4: Создание компонента формы

src/components/ContactForm.tsx
import { useMemo } from 'react';
import { createContactForm } from '@/forms/contact-form';
import { FormField } from '@/components/ui/FormField';

export function ContactForm() {
const form = useMemo(() => createContactForm(), []);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
form.markAsTouched();
await form.validate();

if (form.valid.value) {
console.log('Отправка:', form.value.value);
}
};

return (
<form onSubmit={handleSubmit} className="space-y-4 max-w-md">
<FormField control={form.name} />
<FormField control={form.email} />
<FormField control={form.message} />

<button
type="submit"
disabled={form.invalid.value}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
>
Отправить
</button>
</form>
);
}

Результат

Вы создали форму с:

  • ✅ Типобезопасностью TypeScript
  • ✅ Декларативной валидацией
  • ✅ Автоматическим отображением ошибок
  • ✅ Чистым и минимальным кодом

Следующие шаги