Form 폼
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>
)
}
Form Props
| 프로퍼티 | 설명 | 타입 | 기본값 |
|---|---|---|---|
| model | 폼 데이터 객체 (필수) | Record<string, unknown> | — |
| rules | 유효성 검사 규칙, 키는 필드명 | Record<string, LhFormRule[]> | — |
| labelWidth | 레이블 너비 | string | number | — |
| labelPosition | 레이블 위치 | 'left' | 'right' | 'top' | 'right' |
| inline | 인라인 레이아웃 | boolean | false |
| disabled | 모든 자식 항목 비활성화 | boolean | false |
| size | 자식 항목 크기 | 'large' | 'middle' | 'small' | ConfigProvider 따름 |
| onSubmit | 기본 제출 이벤트 (preventDefault 적용됨) | (event: FormEvent) => void | — |
FormItem Props
| 프로퍼티 | 설명 | 타입 | 기본값 |
|---|---|---|---|
| label | 레이블 문구 | string | — |
| prop | 필드명 (점 경로 지원, model의 키에 대응) | string | — |
| rules | 항목 수준의 규칙 (동명일 때 form 수준을 덮어씀) | LhFormRule[] | — |
| required | 필수 별표 (표시만 하고, 유효성 검사 의미는 rules가 제공) | boolean | false |
| labelWidth | form 수준 레이블 너비 덮어씀 | 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 |
Form 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)를 사용해 비동기 validator의 Promise를 기다리지 않아, 비동기 유효성 검사가 항상 true를 반환합니다. React 버전은 thenable이 완료된 후에 명시적으로 판정합니다. 자세한 내용은 마이그레이션 가이드를 참조하세요.
LhFormItem은 useEffect에서 form에 유효성 검사기를 등록하고, 언마운트 시 자동으로 등록을 취소합니다. 필드를 rules에 미리 선언할 필요가 없습니다. rules에는 없지만 model에는 있는 필드의 초기값은 등록 시점에 채워집니다.
share의 validateFormField는 trigger를 해석하지 않으며, 발생 시점은 이 계층의 연결이 담당합니다. trigger: 'blur' (기본값)는
FormItem이 포커스를 잃을 때 유효성 검사를, trigger: 'change'는 콘텐츠 영역의 change로 유효성 검사를 수행합니다. trigger가 표시되어 있지 않은 규칙은
포커스 상실과 validate()일 때만 실행되며, 입력 도중에는 반복해서 오류가 표시되지 않습니다.
change 유효성 검사는 model 스냅샷이 아닌 이벤트 대상의 새 값을 가져옵니다. React의 상태 업데이트와 이벤트 전달은 같은 틱에 일어나지 않아,
model을 다시 읽으면 한 틱 뒤처집니다. 기본 컨트롤 (input / textarea / 기본 select)의 change 이벤트는
콘텐츠 영역으로 버블링되어 캡처됩니다. 커스텀 컨트롤에는 기본 change 이벤트가 없어 (LhSelect의 옵션 클릭은 click만 발생),
FormItem 컨텍스트의 validateChange(value)로 새 값을 직접 전달합니다.
resetFields()는 model을 초기값 스냅샷으로 되돌리는 외에도 내부 resetVersion을 증가시키며, 각 FormItem은
버전 변화를 감지해 자신의 오류 상태를 비웁니다. 외부 제어 상태는 여전히 직접 동기화해야 합니다 (데모의
setFormData(initial)처럼). React는 Vue의 reactive 연쇄처럼 model을 대신 작성할 수 없습니다.
스타일
import '@lh-ui/react/style/form'