Форма
Составной компонент из LhForm + LhFormItem, предоставляющий валидацию данных, вывод ошибок, выравнивание меток и другие возможности.
Базовое использование
import { useRef, useState } from 'react'
import { LhButton, LhForm, LhFormItem, LhInput, LhSelect } from '@lh-ui/react'
import type { LhFormInstance, LhFormRule } from '@lh-ui/react'
interface FormData {
name: string
style: string
}
const rules: Record<string, LhFormRule[]> = {
name: [{ required: true, message: 'Введите название произведения', trigger: 'blur' }],
style: [{ required: true, message: 'Выберите стиль живописи', trigger: 'change' }],
}
export default function Demo() {
const [formData, setFormData] = useState<FormData>({ name: '', style: '' })
const formRef = useRef<LhFormInstance>(null)
const handleSubmit = async () => {
const valid = await formRef.current?.validate()
if (valid) alert(`Отправлено: ${formData.name} / ${formData.style}`)
}
const handleReset = () => {
formRef.current?.resetFields()
setFormData({ name: '', style: '' })
}
return (
<LhForm ref={formRef} model={formData} rules={rules} labelWidth="80px">
<LhFormItem label="Название произведения" prop="name">
<LhInput
value={formData.name}
onChange={(val) => setFormData((prev) => ({ ...prev, name: val }))}
/>
</LhFormItem>
<LhFormItem label="Стиль живописи" prop="style">
<LhSelect
value={formData.style}
onChange={(val) => setFormData((prev) => ({ ...prev, style: val as string }))}
options={[{ label: 'Тушь', value: 'ink' }]}
/>
</LhFormItem>
<LhFormItem>
<LhButton type="primary" onClick={handleSubmit}>Отправить</LhButton>
<LhButton onClick={handleReset}>Сбросить</LhButton>
</LhFormItem>
</LhForm>
)
}
Props компонента Form
| Проп | Описание | Тип | По умолчанию |
|---|---|---|---|
| model | Объект данных формы (обязательный) | Record<string, unknown> | — |
| rules | Правила валидации; ключи — имена полей | Record<string, LhFormRule[]> | — |
| labelWidth | Ширина меток | string | number | — |
| labelPosition | Положение меток | 'left' | 'right' | 'top' | 'right' |
| inline | Строчное (inline) расположение | boolean | false |
| disabled | Отключить все дочерние элементы | boolean | false |
| size | Размер дочерних элементов | 'large' | 'middle' | 'small' | Из ConfigProvider |
| onSubmit | Нативное событие отправки (уже прошло preventDefault) | (event: FormEvent) => void | — |
Props компонента FormItem
| Проп | Описание | Тип | По умолчанию |
|---|---|---|---|
| label | Текст метки | string | — |
| prop | Имя поля (поддерживает точечный путь, соответствует ключу в model) | string | — |
| rules | Правила на уровне элемента (при совпадении имён переопределяют правила формы) | LhFormRule[] | — |
| required | Звёздочка обязательности (только маркер; семантика валидации задаётся в rules) | boolean | false |
| labelWidth | Переопределяет ширину меток уровня формы | string | number | — |
| errorMessage | Пользовательское сообщение об ошибке | string | — |
| showMessage | Показывать сообщение об ошибке | boolean | true |
LhFormRule
| Проп | Описание | Тип |
|---|---|---|
| required | Обязательное поле | boolean |
| message | Сообщение при ошибке валидации | string |
| trigger | Способ срабатывания | 'blur' | 'change' |
| validator | Пользовательская функция валидации (может возвращать Promise; асинхронная валидация корректно ожидается) | (value, model) => string | undefined | Promise<string | undefined> |
| min / max | Минимальное / максимальное значение (число или дл ина) | number |
| pattern | Проверка по регулярному выражению | RegExp |
Методы ref формы
LhFormInstance (предоставляется через forwardRef + useImperativeHandle):
| Метод | Описание | Возвращаемое значение |
|---|---|---|
| validate | Валидация всех полей | Promise<boolean> |
| validateField | Валидация отдельного поля | Promise<boolean> |
| resetFields | Сброс всех полей к начальным значениям | void |
| resetField | Сброс отдельного поля | void |
const formRef = useRef<LhFormInstance>(null)
const ok = await formRef.current?.validate()
В этой библиотеке и справлен недостаток асинхронной валидации plus: в plus validateField использует Promise.resolve(!message) и не дожидается Promise асинхронного validator, поэтому асинхронная валидация всегда возвращает true. В React-версии явным образом ожидается завершение thenable, и только после этого выносится решение. Подробнее см. в Руководстве по миграции.
LhFormItem регистрирует валидатор в форме внутри useEffect и автоматически отменяет регистрацию при размонтировании. Поля не нужно заранее объявлять в rules — если поле есть в model, но отсутствует в rules, начальное значение фиксируется при регистрации.
Функция validateFormField в share не интерпретирует trigger; момент срабатывания определяется на уровне этой обвязки: trigger: 'blur' (по умолчанию)
валидирует при потере фокуса элементом FormItem, trigger: 'change' — при изменении в области содержимого. Правила без явного trigger
выполняются только при потере фокуса и вызове validate(), поэтому при вводе ошибки не появляются постоянно.
Валидация change использует новое значение цели события, а не снимок model: обновление состояния и диспетчеризация события в React
происходят не в одном такте, поэтому повторное чтение model отставало бы на один такт. События change нативных элементов (input / textarea / нативный select)
всплывают и перехватываются в области содержимого; пользовательские элементы не порождают нативных событий change (нажатие на пункт LhSelect даёт лишь click),
поэтому валидация запускается явно через validateChange(value) из контекста FormItem с передачей нового значения.
Помимо возврата model к снимку начальных значений, resetFields() увеличивает внутренний resetVersion; элементы FormItem
отслеживают изменение версии и очищают собственное состояние ошибки. Внешнее контролируемое состояние нужно синхронизировать самому
(как setFormData(initial) в демо) — React, в отличие от реактивной цепочки Vue, не может обновить model за вас.
Стили
import '@lh-ui/react/style/form'