メインコンテンツへスキップ

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インラインレイアウトbooleanfalse
disabledすべての子項目を無効化booleanfalse
size子項目のサイズ'large' | 'middle' | 'small'ConfigProvider に準拠
onSubmitネイティブ送信イベント(preventDefault 済み)(event: FormEvent) => void—

FormItem Props​

プロパティ説明型デフォルト値
labelラベルテキストstring—
propフィールド名(ドットパスをサポート。model のキーに対応)string—
rulesitem レベルのルール(同名の場合は form レベルを上書き)LhFormRule[]—
required必須の星印(マークの描画のみ。バリデーション semantics は rules が提供)booleanfalse
labelWidthform レベルのラベル幅を上書きstring | number—
errorMessageカスタムエラーメッセージstring—
showMessageエラー通知を表示booleantrue

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 にあるフィールドの初期値は、登録時に補撮されます。

trigger のセマンティクス(React 拡張)

share の validateFormField は trigger を解釈せず、トリガーのタイミングはこのレイヤーが接続します:trigger: 'blur'(デフォルト)は FormItem のフォーカス外れに従ってバリデーションし、trigger: 'change' は内容領域の change に従ってバリデーションします。trigger が未指定のルールは フォーカス外れと validate() の時にのみ実行され、入力中に繰り返しエラーは出ません。

change バリデーションはモデルのスナップショットではなくイベント.target の新しい値を使います —— React の状態更新とイベントディスパッチは 同一タイミングで行われないため、モデルを読み返すと 1 拍遅れます。ネイティブコントロール(input / textarea / ネイティブ select)の change イベントは 内容領域にバブルした時にキャプチャされます。カスタムコントロールにはネイティブ change イベントがなく(LhSelect の項目クリックは click しか発生させない)、 FormItem コンテキストの validateChange(value) を使い、新しい値を能動的に渡してトリガーします。

リセット時のエラークリア(React 拡張)

resetFields() は model を初期値のスナップショットに戻すほか、内部 resetVersion をインクリメントし、各 FormItem が バージョン変化を検知して自身のエラー状態をクリアします。外部の制御状態は別途同期が必要です(demo の setFormData(initial) など) —— React は Vue の reactive なチェーンのように model を代わりに書き換えることはできません。

スタイル​

import '@lh-ui/react/style/form'