4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-03-16

基本的には、shadcn/uiを使ったサジェストフォームの簡単なサンプルとして、下記のデモサイトがありますが、こちらをコピペすればよいです。
https://www.armand-salle.fr/post/autocomplete-select-shadcn-ui

このサイトを運営しているarmandさんのgithubのソースコード

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

完成図

suggestのロジックの変更

上記実装で使われている、shadcn/uiのComboboxcmdkのComboboxをwrapしたものです。suggestのロジックを変更したかった(+日本語だと上手くいかない気がした)ので、cmdkのドキュメントに記載されている通りfilterに独自実装を追加してます。sortとかも出来るようです。

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

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

keyの追加時の処理

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>
        {specKey?.valueType === SpecValueType.TEXTAREA ? ((
          <Input
            aria-invalid={!!form.formState.errors.value}
            {...form.register("value")}
          />
        )}
      </FormControl>
    </FormItem>

そして、下記のようにzodのsuperRefineを使ってvalidateします。このとき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

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

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

まとめ

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

4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?