Formulario
Componente compuesto formado por LhForm + LhFormItem, que aporta validación de datos, mensajes de error, alineación de etiquetas y otras capacidades.
Uso básico
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: 'Introduce el nombre de la obra', trigger: 'blur' }],
style: [{ required: true, message: 'Selecciona el estilo de pintura', 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(`Envío correcto: ${formData.name} / ${formData.style}`)
}
const handleReset = () => {
formRef.current?.resetFields()
setFormData({ name: '', style: '' })
}
return (
<LhForm ref={formRef} model={formData} rules={rules} labelWidth="80px">
<LhFormItem label="Nombre de la obra" prop="name">
<LhInput
value={formData.name}
onChange={(val) => setFormData((prev) => ({ ...prev, name: val }))}
/>
</LhFormItem>
<LhFormItem label="Estilo de pintura" prop="style">
<LhSelect
value={formData.style}
onChange={(val) => setFormData((prev) => ({ ...prev, style: val as string }))}
options={[{ label: 'Tinta china', value: 'ink' }]}
/>
</LhFormItem>
<LhFormItem>
<LhButton type="primary" onClick={handleSubmit}>Enviar</LhButton>
<LhButton onClick={handleReset}>Restablecer</LhButton>
</LhFormItem>
</LhForm>
)
}
Form Props
| Prop | Descripción | Tipo | Predeterminado |
|---|---|---|---|
| model | Objeto de datos del formulario (obligatorio) | Record<string, unknown> | — |
| rules | Reglas de validación; la clave es el nombre del campo | Record<string, LhFormRule[]> | — |
| labelWidth | Ancho de la etiqueta | string | number | — |
| labelPosition | Posición de la etiqueta | 'left' | 'right' | 'top' | 'right' |
| inline | Disposición en línea | boolean | false |
| disabled | Desactiva todos los elementos hijos | boolean | false |
| size | Tamaño de los elementos hijos | 'large' | 'middle' | 'small' | Sigue ConfigProvider |
| onSubmit | Evento de envío nativo (ya con preventDefault) | (event: FormEvent) => void | — |
FormItem Props
| Prop | Descripción | Tipo | Predeterminado |
|---|---|---|---|
| label | Texto de la etiqueta | string | — |
| prop | Nombre del campo (admite ruta de puntos; corresponde a la clave de model) | string | — |
| rules | Reglas a nivel de item (si coinciden, sustituyen a las de nivel form) | LhFormRule[] | — |
| required | Asterisco de obligatorio (solo el marcado visual; la semántica de validación la aportan rules) | boolean | false |
| labelWidth | Sustituye al ancho de etiqueta de nivel form | string | number | — |
| errorMessage | Mensaje de error personalizado | string | — |
| showMessage | Muestra el mensaje de error | boolean | true |
LhFormRule
| Prop | Descripción | Tipo |
|---|---|---|
| required | Si es obligatorio | boolean |
| message | Mensaje de error de validación | string |
| trigger | Momento de disparo | 'blur' | 'change' |
| validator | Función de validación personalizada (puede devolver una Promise; la validación asíncrona se espera correctamente) | (value, model) => string | undefined | Promise<string | undefined> |
| min / max | Valor mínimo / máximo (número o longitud) | number |
| pattern | Validación con expresión regular | RegExp |
Métodos del ref de Form
LhFormInstance (expuesto mediante forwardRef + useImperativeHandle):
| Método | Descripción | Valor de retorno |
|---|---|---|
| validate | Valida todos los campos | Promise<boolean> |
| validateField | Valida un solo campo | Promise<boolean> |
| resetFields | Restablece todos los campos a su valor inicial | void |
| resetField | Restablece un solo campo | void |
const formRef = useRef<LhFormInstance>(null)
const ok = await formRef.current?.validate()
Esta biblioteca corrige el defecto de validación asíncrona de plus: el validateField de plus usa Promise.resolve(!message), que no espera a la Promise de un validador asíncrono, por lo que la validación asíncrona siempre devuelve true. La versión React espera explícitamente a que se resuelva el thenable antes de juzgar. Consulta la Guía de migración.
LhFormItem registra su validador en el formulario dentro de useEffect y se da de baja automáticamente al desmontarse. No hace falta declarar los campos en rules de antemano: los campos que existen en model pero no en rules reciben una validación de su valor inicial en el momento del registro.
La función validateFormField del núcleo compartido no interpreta trigger; el momento de disparo lo decide esta capa: trigger: 'blur' (predeterminado)
valida cuando FormItem pierde el foco y trigger: 'change' valida cuando hay un cambio en la zona de contenido. Las reglas que no declaran trigger
solo se ejecutan al perder el foco y al llamar a validate(), de modo que no se muestran errores constantemente mientras se escribe.
La validación change toma el nuevo valor del destino del evento, no la instantánea de model: en React, la actualización del estado
y el despacho del evento no ocurren en el mismo ciclo, y leer de nuevo model iría un ciclo por detrás. El evento change de los controles nativos
(input / textarea / select nativo) se captura al burbujear a la zona de contenido; los controles personalizados no tienen evento change nativo
(la pulsación de una opción de LhSelect solo produce un click), por lo que se dispara manualmente con el nuevo valor a través del validateChange(value)
del contexto de FormItem.
resetFields(), además de devolver model a la instantánea de sus valores iniciales, incrementa el resetVersion interno; cada FormItem
detecta el cambio de versión y limpia su propio estado de error. El estado controlado externo debe sincronizarse por separado (como el
setFormData(initial) del demo): React no puede escribir model en el lugar de la cadena reactiva de Vue.
Estilos
import '@lh-ui/react/style/form'