shadcn/ui+react-hook-form+zodを使ったサジェストフォームの作り方
基本的には、下記のサイトをコピペしたらOKです。
運営されているarmandさんのgithubのソースコード
冗談はさておき、今個人開発しているプレイスオブというサイトの物件のスペックを入力する画面でこの実装をほぼコピペさせてもらっているのですが、何点か追加実装が必要だったのでメモしておきます。
suggestのロジックの変更
上記実装で使われている、shadcn/uiのCombobox はcmdkの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一覧の変換箇所
確定したkeyの変換箇所
詳しくは下記のzenn記事にあります。
i18n一般
zodのi18n
サーバーからデータを取得
サーバーからデータを取得する場合は、こちらのzenn記事が参考になるかと思います
完成図
まとめ
マスターデータをまだそこまで用意できていないのですが、もっと完成度を高めて物件のありとあらゆるスペックが入力できるフォームにしていきたいので、何かあれば教えて下さい。
Discussion