😺
input[type="number"]が不便すぎるので改善したい
不便な点
マウスでスクロースすると数字が変化してしまう
右側の▲▼いらない
右側の▲▼はCSSを弄ればどうにかなりますが、スクロースはどうしようもないので、別のやり方を模索します。
改善案
Next.13のAppRouter + react-hook-form + shadcnで作ってますが、基本的に他のやり方でも考え方は同じです
"use client";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
const formSchema = z.object({
name: z
.string({ required_error: "名前の入力は必須です" })
.max(60, { message: "名前は60字までです" }),
age: z
.number({ required_error: "年齢の入力は必須です" })
.min(18, { message: "未成年は登録できません" })
.max(125, { message: "存在しない年齢は入力できません" }),
});
export const AgeForm = () => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
age: 0,
},
});
function onSubmit(values: z.infer<typeof formSchema>) {
// Do something with the form values.
// ✅ This will be type-safe and validated.
console.log(values);
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>名前</FormLabel>
<FormControl>
<Input type="text" placeholder="田中太郎" {...field} />
</FormControl>
<FormDescription>
名前はフォローされたユーザーに表示されます
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="age"
render={({ field }) => (
<FormItem>
<FormLabel>年齢</FormLabel>
<FormControl>
<Input
type="text"
inputMode="numeric"
placeholder="20"
{...field}
onChange={(e) => {
const value = e.target.value;
const onlyNumberRegex = new RegExp(/^[0-9]*$/);
if (onlyNumberRegex.test(value)) {
field.onChange(Number(value));
}
}}
/>
</FormControl>
<FormDescription>年齢は公開されません</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
);
};
ポイント1. 数値以外の入力は無視する
インプットフォームに変更があった場合、入力された文字を見ます。
その文字が数値ならインプットフォームに反映しますが、それ以外の場合は反映しません。
<Input
type="text"
inputMode="numeric"
placeholder="20"
{...field}
onChange={(e) => {
const value = e.target.value; // 入力値
const onlyNumberRegex = new RegExp(/^[0-9]*$/); // 数値ならture
if (onlyNumberRegex.test(value)) {
field.onChange(Number(value)); // 数値を反映する
}
}}
/>
ポイント2. 初期値に数値を入れておく
初期値を入れておかないと、不正な文字列の入力ができます。
そこで初期値を入れておきます
export const AgeForm = () => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
age: 0, // 数値以外の値を入力させないために初期値を設定する
},
});
これで不正な文字列の入力ができなくなります。
(かわりに初期値が入ってしまって気持ち悪い...)
Discussion