1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

react-hook-formのControllerとReactSelectで複数選択フォームを作る

1
Posted at

はじめに

<input> や <textarea> はreact-hook-formの register() で簡単に管理できますが、
ReactSelect などのカスタムUIコンポーネントには使えません。
そこで Controller を使います。

Controllerとは

Controller は、カスタムUIコンポーネントをreact-hook-formで管理するためのラッパーです。

<Controller
  name="skill_ids"   // フォームのフィールド名
  control={control}  // useFormが返す管理オブジェクト
  rules={{ validate: (v) => v.length > 0 || "選択してください" }}
  render={({ field }) => (
    <ReactSelect
      onChange={field.onChange}
      value={field.value}
    />
  )}
/>

controlとは

useForm() を呼び出すと返ってくる管理オブジェクトです。

const {
  register,
  handleSubmit,
  control,  // ← これ
  formState: { errors },
} = useForm<FormData>();

フォーム全体の状態(各フィールドの値・バリデーション状態など)を保持しており、
Controller に渡すことで「このフォームに属してください」と伝えます。

fieldとは

render の引数として Controller が渡してくれるオブジェクトです。

プロパティ 役割
field.value フォームが持つ現在の値
field.onChange 値が変わったときフォームに伝える関数
field.onBlur フォーカスが外れたときフォームに伝える関数

renderとは

Controller が「何を描画するか」を指定するプロパティです。
関数を渡すと、Controller がその関数を呼び出してUIを描画します。

<Controller
  name="skill_ids"
  control={control}
  render={({ field }) => (  // ← ここに描画したいコンポーネントを書く
    <ReactSelect ... />
  )}
/>

ReactSelectとは

クリックだけで複数選択できるドロップダウンUIライブラリです。

npm install react-select
import ReactSelect from "react-select";

<ReactSelect
  isMulti
  options={[
    { label: "React", value: 1 },
    { label: "TypeScript", value: 2 },
  ]}
  onChange={(selected) => console.log(selected)}
/>

options は必ず { label, value } 形式で渡します。

ControllerとReactSelectを組み合わせる

// フォームの型定義
type FormData = {
  skill_ids: number[];
};
  const {
    register,
    handleSubmit,
    reset,
    control,
    formState: { errors },
  } = useForm<FormData>({
    defaultValues: {
      skill_ids: [],
    },
  });

//〜〜〜〜〜〜〜〜〜〜〜〜〜〜省略

<Controller
  name="skill_ids"
  control={control}
  rules={{
    validate: (v) => v.length > 0 || "好きな技術を選択してください",
  }}
  render={({ field }) => (
    <ReactSelect
      isMulti
      options={skills?.map((s) => ({ label: s.name, value: s.id }))}

      // フォームの number[] → ReactSelectの {label, value}[] に変換
      value={skills
        ?.filter((s) => field.value.includes(s.id))
        .map((s) => ({ label: s.name, value: s.id }))}

      // ReactSelectの {label, value}[] → フォームの number[] に変換
      onChange={(selected) => {
        field.onChange(selected.map((s) => s.value));
      }}
    />
  )}
/>

選択から表示までの流れ

  1. 選択肢をクリック
    ↓
  2. onChange が呼ばれる
    field.onChange([1, 2])
    ↓
  3. react-hook-form の内部状態が更新される
    ↓
  4. 再レンダリングが発生する
    ↓
  5. 新しい field.value が render に渡される
    field.value = [1, 2]
    ↓
  6. value が再計算される
    skills.filter((s) => [1, 2].includes(s.id))
    → [{ label: "React", value: 1 }, { label: "TypeScript", value: 2 }]
    ↓
  7. ReactSelect に選択済み項目として表示される
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?