🎲
shadcnとzodを使って電話番号のフォームを作る
shadcn + react-hook-form + zodを使用して電話番号を入力するフォームを実装した時のメモ
環境
- NextJS: 14.1.0
- zod: ^3.22.4
- react-phone-number-input: ^3.3.12
- react-hook-form: ^7.49.2
react-phone-number-input
このライブラリを使えば大体解決になる。
内部的にはlibphonenumber-jsという軽量なライブラリを使っている
libphonenumber-jsはGoogleが提供しているgoogle/libphonumberを拡張したもの
install
pnpm add react-phone-number-input
usage
react-phone-number-inputは様々なPhoneInputコンポーネントを提供している
インポートパスには注意が必要
shadcn/react-hook-formと使う
shadcnと使うならFormLabelやFormMessageと組み合わせたいのでFormField内にまとめておいてしまうと良い
自分が使った時は日本の電話番号に限定して良かったので、country=”JP”
で固定しておく
import PhoneInput from "react-phone-number-input/react-hook-form-input";
const PhoneInputFormField = () => {
const { control } = useFormContext();
return (
<FormField
control={control}
name="phoneInput"
render={({ field: {onChange, value} }) => (
<FormItem className={cn("flex flex-col gap-1", className)}>
<div className="inline-flex items-center gap-2">
<FormLabel>phone number</FormLabel>
</div>
<div className="inline-flex w-full items-center gap-5">
<FormControl>
<PhoneInput
country="JP"
required={false}
className="h-full rounded-md border-none bg-gray-100 px-3 py-4"
placeholder"090-1234-5678"
onChange={onChange}
defaultValue={value}
/>
</FormControl>
</div>
<FormMessage />
</FormItem>
)}
/>
);
};
※このIssueはreact-phone-number-input/react-hook-form-inputのPhoneInputを使ってそうなったというバグを報告している(既に修正済み)が、react-phone-number-input/inputのPhoneInputを使うと同じようになる
国を入力する
公式ドキュメントにあるように、countryをユーザーに入力させるPhoneInputWithCountryコンポーネントも別で提供されている
import 'react-phone-number-input/style.css'
import PhoneInput from 'react-phone-number-input'
function Example() {
// `value` will be the parsed phone number in E.164 format.
// Example: "+12133734253".
const [value, setValue] = useState()
return (
<form onSubmit={handleSubmit(...)}>
<PhoneInputWithCountry
name="phoneInputWithCountrySelect"
control={control}
rules={{ required: true }} />
<button type="submit">
Submit
</button>
</form>
)
}
utils
react-phone-number-inputにはlibphonenumber-js由来のユーティリティ関数がExportされている
フォームで入力した値をローカルな形式に変換する
formatPhoneNumber
を使うと良い
import { formatPhoneNumber } from "react-phone-number-input";
const { getValues } = useFormContext()
const values = getValues()
console.log(values.phoneInput) // "+819012345678"
const phone = formatPhoneNumber(values.phoneInput) // 自動でローカライズされる
console.log(phone) // 090-1234-5678
Zodで電話番号のバリデーションをする
isValidPhoneNumber
は長さや形式が電話番号であるかを判定する
長さだけを判定するisPossiblePhoneNumber
も必要に応じて使えるだろう
import { isValidPhoneNumber } from "libphonenumber-js";
import { z } from "zod";
export const SampleSchema = z.object({
PhoneInput: z
.string({
required_error: "電話番号を入力してください",
invalid_type_error: "電話番号を入力してください",
})
.refine(isValidPhoneNumber, { message: "電話番号が正しくありません" })
});
reference
Discussion