クライアントAPI
@k8ordo/formから使えるフックとコンポーネント、型の一覧です。どれもClient Componentの中で使います。サーバーで呼ぶ関数は「サーバーAPI」のページにまとめています。
このページの内容
useForm
import元 @k8ordo/form
formFieldsが作った入力欄の情報を、<form>とその中の入力欄につなぎます。
useForm(fields: FormFields, state?: FormState): UseFormReturn引数
fieldsFormFields- Server Componentで
formFieldsを呼んだ結果です。propsで受け取ったものを、そのまま渡します。 stateFormState- Server Actionが返した
FormStateで、useActionStateが返す1つ目の値です。送信先のアクションが無いGETのフォームでは省きます。
戻り値
UseFormReturn — <form>に展開するpropsと、入力欄ごとの表示を返す関数をまとめたオブジェクト。
フィールド
props{ onSubmit, onBlur, onInput, onReset, ref }<form>に展開するイベントハンドラ(onSubmit、onBlur、onInput、onReset)とref。ほかの要素には付けません。field(path) => FieldView- パスを渡すと、その入力欄の
FieldViewを返します。入れ子の欄はuser.emailのようにドットでつなぎます。 array(path) => ArrayView- 繰り返しの行を持つ配列のパスを渡すと、
ArrayViewを返します。 formErrorFormErrorView- どの入力欄にも属さないエラーと、それを表示する要素に展開するprops。
isDirtyboolean- どれかの入力欄の値が、描画したときから変わっていれば
true。行を足したり消したりした場合も含みます。
注意
- 入力された値はDOMが持っていて、Reactのstateには写しません。そのため、キーを押すたびに再描画されることはありません。
- 展開したあとに自分の
onSubmitを書くと、props.onSubmitを上書きしてしまいます。自分の処理の中からform.props.onSubmit(event)を呼んでください。 noValidateはJavaScriptで付けます。サーバーが描いたHTMLには入らないので、読み込みの途中でもブラウザが入力を確かめます。- 新しい
stateが届くと、画面の順で最初に失敗した欄へフォーカスを移します。stateは中身とtokenで見分けるので、手で作るときはtokenも変えます。
talk-form.tsxconst [state, formAction] = useActionState(createTalk, {});
const form = useForm(fields, state);
const title = form.field('title');
<form {...form.props} action={formAction}>
<input {...title.input} />
{title.error !== undefined && <p>{title.error}</p>}
</form>作り方の流れは「はじめる」を、エラーの表示の仕方は「エラーを表示する」を見てください。
FieldView
import元 @k8ordo/form
form.field()が返す、1つの入力欄の表示に必要な値です。
type FieldView = {
input: FieldInput;
error: string | undefined;
invalid: boolean;
required: boolean;
};フィールド
inputFieldInput- 入力欄に展開する属性。
nameやtype、required、maxLengthなどで、送信に失敗したあとは送った値がdefaultValueに入ります。 errorstring | undefined- いま表示するエラーの文言。ブラウザが確かめた結果か、サーバーが返したもので、無ければ
undefined。 invalidbooleanerrorがあるときにtrue。aria-invalidなどに渡します。requiredboolean- スキーマが空の送信を受け付けないときに
true。ラベルに必須の印を付けるのに使います。
注意
- サーバーのエラーは、その欄を書き換えるまで残ります。ブラウザの検査で出たエラーがあれば、そちらを優先します。
z.stringbool()の欄だけは、inputに送る文字列のvalueが入ります。
ArrayView
import元 @k8ordo/form
form.array()が返す、繰り返しの行の表示に必要な値です。
type ArrayView = {
rows: RowView[];
add: () => void;
canAdd: boolean;
canRemove: boolean;
error: string | undefined;
errorProps: { id: string; tabIndex: -1 };
};フィールド
rowsRowView[]- いま表示している行。1行ずつが
RowViewです。 add() => void- 末尾に行を1つ足します。
canAddboolean- スキーマの
.max()に届いていなければtrue。 canRemoveboolean- スキーマの
.min()より多ければtrue。 errorstring | undefined- 配列そのもののエラー(行が多すぎる、または少なすぎる)。どの行の入力欄にも出ません。
errorProps{ id: string; tabIndex: -1 }errorを表示する要素に展開するidとtabIndex={-1}。送信に失敗したとき、ここへフォーカスが移ります。
order-form.tsxconst items = form.array('items');
{items.rows.map((row) => (
<div key={row.key}>
<input {...row.field('name').input} />
{items.canRemove && (
<button onClick={row.remove} type="button">
Remove
</button>
)}
</div>
))}
{items.canAdd && (
<button onClick={items.add} type="button">
Add a row
</button>
)}RowView
import元 @k8ordo/form
繰り返しの1行です。
type RowView = {
key: string;
index: number;
field: (itemKey?: string) => FieldView;
remove: () => void;
};フィールド
keystring- 行を見分けるための値で、
keyに渡します。行を消しても、ほかの行のkeyは変わりません。 indexnumber- いまの位置。0から数えます。
field(itemKey?: string) => FieldView- 行の中の入力欄のキーを渡すと、その欄の
FieldViewを返します。nameはitems[0].nameのようになります。文字列の配列では、引数を省きます。 remove() => void- この行を消します。
注意
- 行の中の欄のキーは、パスと違って型で確かめません。書き間違えると、描画したときに例外を投げます。
FormErrorView
import元 @k8ordo/form
form.formErrorの型です。スキーマ全体に付けた.refine()のエラーのように、どの入力欄にも属さないエラーを持ちます。
type FormErrorView = {
message: string | undefined;
props: { id: string; tabIndex: -1 };
};フィールド
messagestring | undefinedstate.formErrorの文言。無ければundefined。props{ id: string; tabIndex: -1 }- 文言を表示する要素に展開する
idとtabIndex={-1}。
useAsyncCheck
import元 @k8ordo/form
入力欄から離れたときに、その欄の値をサーバーに問い合わせます。名前がすでに使われているかどうかのように、サーバーにしか分からないことを確かめるのに使います。
useAsyncCheck(
check: (value: string) => Promise<string | undefined>,
): AsyncCheck引数
check(value: string) => Promise<string | undefined>- 値を受け取り、表示する文言を返す関数。問題が無ければ
undefinedを返します。Server Actionをそのまま渡せます。
戻り値
AsyncCheck — 入力欄に展開するpropsと、問い合わせの途中かどうか。
フィールド
props{ onBlur, ref }- 入力欄に展開する
onBlurとref。field().inputと並べて展開します。 isCheckingboolean- 返事を待っている間は
true。送信ボタンを押せないようにするのに使います。
注意
- 返事は
setCustomValidityで欄に付くので、ほかのエラーと同じerrorに出ます。 - 返事が前後して届いても、最後に問い合わせた値の返事だけを使います。前と同じ値のまま欄を離れても、問い合わせ直しません。
- 欄を空にして離れると、前の返事は消えます。関数が失敗したときは何も付けず、送信後のサーバーの検証に任せます。
slug-field.tsxconst slug = form.field('slug');
const taken = useAsyncCheck(checkSlugAvailable);
<input {...slug.input} {...taken.props} />
<button disabled={taken.isChecking} type="submit">Save</button>HiddenValue
import元 @k8ordo/form
リッチテキストエディタのように<input name>を描かないコンポーネントの値を、フォームの送信に載せます。
<HiddenValue name={string} value={string} />引数
namestring- 送信するときの名前。スキーマでのパスと同じにします。
valuestring- 送信する値。コンポーネントのstateをそのまま渡します。
注意
- 値が変わるたびに
inputイベントを出すので、複数の欄にまたがるルールやisDirtyが、キー入力と同じように気づきます。 - リセットしても値は戻りません。値は呼び出し側のstateなので、そちらも戻します。
post-form.tsx<Editor onChange={setBody} value={body} />
<HiddenValue name="body" value={body} />FormFields
import元 @k8ordo/form
formFieldsが返し、useFormが受け取る値の型です。中身はJSONなので、Server Componentからpropsで渡せます。
type FormFields<FieldPath, ArrayPath, StringCheckboxPath> = {
fields: Record<FieldPath, DerivedField>;
arrays: Record<ArrayPath, DerivedArray>;
rules: DerivedRule[];
dropped: DroppedCheck[];
};フィールド
fieldsRecord<FieldPath, DerivedField>- パスごとの入力欄の情報。入力欄の属性と、検証ごとの文言を持ちます。
arraysRecord<ArrayPath, DerivedArray>- パスごとの繰り返しの行の情報。行数の上限と下限と、1行分の入力欄を持ちます。
rulesDerivedRule[]defineFormで宣言した、複数の入力欄にまたがるルール。文言はformFieldsを呼んだ時点で決まっています。droppedDroppedCheck[]- HTMLの属性で表せないため、ブラウザでは確かめない検証の一覧。サーバーでは確かめます。
注意
- 型引数は、欄のパス、配列のパス、
z.stringbool()の欄のパスの3つです。formFieldsの戻り値から推論されるので、ふつうは書きません。 @k8ordo/formと@k8ordo/form/serverのどちらからもimportできます。