🌊

shadcn/ui+react-hook-form+zodを使ったサジェストフォームの作り方

2024/03/16に公開

基本的には、下記のサイトをコピペしたらOKです。
https://www.armand-salle.fr/post/autocomplete-select-shadcn-ui

運営されているarmandさんのgithubのソースコード

冗談はさておき、今個人開発しているプレイスオブというサイトの物件のスペックを入力する画面でこの実装をほぼコピペさせてもらっているのですが、何点か追加実装が必要だったのでメモしておきます。

suggestのロジックの変更

上記実装で使われている、shadcn/uiのComboboxcmdkのComboboxをwrapしたものです。suggestのロジックを変更したかった(+日本語だと上手くいかない気がした)ので、cmdkのドキュメントに記載されている通りfilterに独自実装を追加してます。sortとかも出来るようです。
https://github.com/armandsalle/my-site/blob/main/src/components/autocomplete.tsx#L88 を下記のようにします

<CommandPrimitive
  filter={(value, search) => {
    if (value.includes(search)) return 1
    return 0
  }}
...
/>

keyの値でvalueのバリデーションをする

keyの追加時の処理

react-hook-formとshadcnを使うときは、こちらのshadcnのドキュメントに記載されているようにします。

以下は、keyとvalueの2つのフォームがあり、keyの値でvalueの値をvalidationする必要するときの実装例です。suggestをするkeyがhandleKeyDownなどで確定するときに、onValueChangeで親のComponentの関数を呼び出していますが、そのときに、

 onValueChange={(key: SuggestKey) => {
    form.setValue("suggestKeyId", key.suggestKeyId)
    form.setValue("suggestValueType", key.valueType)
}}

のようにreact-hook-formのsetValue関数を使い、確定したkeyをformに追加します。なお、このときに下記のようにreact-hook-formのformが定義されています。

  type Inputs = z.infer<typeof SuggestKeyFormSchema>
  const form = useForm<Inputs>({
    resolver: zodResolver(SuggestKeyFormSchema),
    defaultValues: defaultValues,
  })

valueの追加時の処理

下記のようにvalueをregisterします。

    <FormItem>
      <FormControl>
          <Input
            aria-invalid={!!form.formState.errors.value}
            {...form.register("value")}
          />
      </FormControl>
    </FormItem>

react-hook-formのmode次第ですが、zodのvalidationが発火されるときは下記のようにsuperRefineを使います。このときkeyを追加したときのvalueTypeが使われます。

export const SuggestKeyFormSchema = z
  .object({
    suggestKeyId: z.string().min(1),
    valueType: SuggestValueTypeSchema,
    value: z.string().min(1),
  })
  .superRefine((values, ctx) => {
    if (
      values.valueType === SuggestValueTypeSchema.STRING &&
      values.value.length >= 20
    ) {
      ctx.addIssue({
        message: "need to be less than 20 words",
        code: z.ZodIssueCode.custom,
        path: ["value"],
      })
    }

   ....
  })

i18n

ちなみに、keyはi18nのファイルを参照するようにして表示しています。

suggest一覧の変換箇所
https://github.com/armandsalle/my-site/blob/main/src/components/autocomplete.tsx#L36

確定したkeyの変換箇所
https://github.com/armandsalle/my-site/blob/main/src/components/autocomplete.tsx#L128

詳しくは下記のzenn記事にあります。
i18n一般
https://zenn.dev/k0kishima/articles/956ba3f3dc9629
zodのi18n
https://zenn.dev/aiji42/articles/171f26af4e5b5c

サーバーからデータを取得

サーバーからデータを取得する場合は、こちらのzenn記事が参考になるかと思います
https://zenn.dev/mktu/articles/2f9166374d09a0

完成図

まとめ

マスターデータをまだそこまで用意できていないのですが、もっと完成度を高めて物件のありとあらゆるスペックが入力できるフォームにしていきたいので、何かあれば教えて下さい。

Discussion