@k8ordo/form

クライアントAPI

@k8ordo/formから使えるフックとコンポーネント、型の一覧です。どれもClient Componentの中で使います。サーバーで呼ぶ関数は「サーバーAPI」のページにまとめています。

このページの内容

useForm

import元 @k8ordo/form

formFieldsが作った入力欄の情報を、<form>とその中の入力欄につなぎます。

ts
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.tsx
const [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つの入力欄の表示に必要な値です。

ts
type FieldView = {
  input: FieldInput;
  error: string | undefined;
  invalid: boolean;
  required: boolean;
};

フィールド

inputFieldInput
入力欄に展開する属性。nameやtype、required、maxLengthなどで、送信に失敗したあとは送った値がdefaultValueに入ります。
errorstring | undefined
いま表示するエラーの文言。ブラウザが確かめた結果か、サーバーが返したもので、無ければundefined。
invalidboolean
errorがあるときにtrue。aria-invalidなどに渡します。
requiredboolean
スキーマが空の送信を受け付けないときにtrue。ラベルに必須の印を付けるのに使います。

注意

  • サーバーのエラーは、その欄を書き換えるまで残ります。ブラウザの検査で出たエラーがあれば、そちらを優先します。
  • z.stringbool()の欄だけは、inputに送る文字列のvalueが入ります。

ArrayView

import元 @k8ordo/form

form.array()が返す、繰り返しの行の表示に必要な値です。

ts
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.tsx
const 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行です。

ts
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()のエラーのように、どの入力欄にも属さないエラーを持ちます。

ts
type FormErrorView = {
  message: string | undefined;
  props: { id: string; tabIndex: -1 };
};

フィールド

messagestring | undefined
state.formErrorの文言。無ければundefined。
props{ id: string; tabIndex: -1 }
文言を表示する要素に展開するidとtabIndex={-1}。

useAsyncCheck

import元 @k8ordo/form

入力欄から離れたときに、その欄の値をサーバーに問い合わせます。名前がすでに使われているかどうかのように、サーバーにしか分からないことを確かめるのに使います。

ts
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.tsx
const 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>を描かないコンポーネントの値を、フォームの送信に載せます。

ts
<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で渡せます。

ts
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できます。
k8ordo

Baselineに入った機能を、制限なく使うReactのライブラリ群。

© 2026 k8o — MIT License

組版:Noto Sans JP / M PLUS 2