useFormを作る

入力欄の数だけ state が増える

出品フォームには商品名と価格があります。素直に書くと、こうなります。

const [title, setTitle] = useState(""); const [price, setPrice] = useState("");

ここに説明文とカテゴリと画像 URL が足されると、state が5つ、ハンドラも5つです。しかも出品フォームと、あとで作るプロフィール編集フォームで、同じ形のコードがまた並びます。前のレッスンの重複と同じ話です。

値をまとめて1つの state にする

入力欄ごとに state を分けず、オブジェクト1つで持ちます。

const [values, setValues] = useState({ title: "", price: "" });

書き換えるときは、入力欄の name を手がかりにします。

function handleChange(e: ChangeEvent<HTMLInputElement>) { const { name, value } = e.target; setValues((prev) => ({ ...prev, [name]: value })); }

[name] は「変数 name の中身をキーにする」という書き方です。name="title" の欄なら title が、name="price" の欄なら price が書き換わります。ハンドラは1つで、入力欄が何個あっても足りるわけです。

...prev を忘れると、打った欄以外が消えます。前の値を広げてから上書きする、この順番が要ります。

useForm として切り出す

この形は、どのフォームでも同じです。だから useItems と同じようにフックへ出します。

export function useForm(initial: FormValues) { const [values, setValues] = useState(initial); function handleChange(e: ChangeEvent<HTMLInputElement>) { ... } function reset() { setValues(initial); } return { values, handleChange, reset }; }

返すのは値だけではありません。handleChange のような関数も一緒に返します。カスタムフックが返すのは「値と、それを扱う道具の一式」です。送信のあとに空へ戻す reset も、フォームなら必ず要るのでここに入れておきます。

演習

出品フォームの入力管理を useForm へ移します。

ヒント

App.tsx
useForm.ts
App.tsx
useForm.ts
プレビュー

できているか

  • 商品名を打つと preview に出る
  • 価格を打って出品するとリストに増える