Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React Hook Form と Zod によるフォームバリデーション
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
SAW
November 07, 2025
Programming
77
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
React Hook Form と Zod によるフォームバリデーション
フロントエンド勉強会 in 大阪 の発表資料です。
SAW
November 07, 2025
More Decks by SAW
See All by SAW
Makefile 入門
azuki
0
87
Effortless API Documentation with Scribe
azuki
0
85
Laravelで手軽にAPIドキュメントを生成する ― Scribe活用術
azuki
0
56
🪝 便利な Property Hooks を 使ってみよう 🪝
azuki
0
93
決済システム超初心者が Stripe に入門している話
azuki
0
140
PHP で form-data を POST 以外のメソッドで受け取るには?
azuki
0
90
PHP で学ぶ OAuth 入門
azuki
2
1.4k
EditorConfig を使ってみよう
azuki
1
130
Symfony でサクッと作る REST API サーバー
azuki
1
290
Other Decks in Programming
See All in Programming
運用ダッシュボードの設計を誰も教えてくれないのだけどみなさんどうしてるんですか? - チームに監視するという文化を根付かせるための第一歩を踏みたい -
satoshi256kbyte
1
130
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
570
5分で問診!Composer セキュリティ健康診断
codmoninc
0
1.1k
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
130
Go 1.27 における memory allocation の高速化
andpad
0
260
React本体のコードリーディング
high_g_engineer
1
150
How I Won Prize Money at a Hackathon Using Codex and Symphony Alpha
yasei_no_otoko
0
110
Android CLI
fornewid
0
230
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
12
19k
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
150
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
190
AI Readyの正体はデータマネジメントだ メダリオン2.0の最前線
freee
PRO
0
320
Featured
See All Featured
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
390
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Amusing Abliteration
ianozsvald
1
250
Chasing Engaging Ingredients in Design
codingconduct
0
280
Build your cross-platform service in a week with App Engine
jlugia
234
19k
Git: the NoSQL Database
bkeepers
PRO
432
67k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
210
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
240
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
570
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Transcript
3FBDU)PPL'PSNͱ;PEʹΑΔ ϑΥʔϜόϦσʔγϣϯ ϑϩϯτΤϯυษڧձJOେࡕ 4"8
$(whoami) w ࢯ໊Ճ౻फҰ ࡀ w ϋϯυϧωʔϜ4"8 w 9 چ5XJUUFS
!B[VLJ@FBUFS w ؔͷ*5ΤϯδχΞίϛϡχςΟͷ͔͠ ࣗশ w େࡕࡏॅɾѪग़ w ಘҙ8FCΞϓϦέʔγϣϯ։ൃ w -BSBWFM 7VF w ॴଐ༗ݶձࣾΞϦʔϓ 2 ඇৗ༻ίοΫͷதʹ খ͞ͳίοΫ͞Μೖ͍ͬͯͳ͍ ࠓͷ໎ݴ
ϑΥʔϜόϦσʔγϣϯͷඞཁੑ w ϢʔβʔͷೖྗϛεΛϑΥʔϜͷૹ৴લʹݕग़ w ೖྗඞਢͷ߲͕ೖྗ͞Ε͍ͯͳ͍ w ϝʔϧΞυϨε༣ศ൪߸ͳͲͷೖྗܗ͕ࣜਖ਼͘͠ͳ͍ w ೖྗ͞Εͨจࣈ͕ࢦఆͷൣғʹऩ·͍ͬͯͳ͍ w
Ͳͷ߲ʹΤϥʔ͕͋Δ͔Ϣʔβʔʹࢹ֮తʹೝࣝͤ͞Δ w ೖྗΤϥʔͷ͋Δ߲ʹରͯ͠Τϥʔ༰ͷϝοηʔδΛදࣔ w ϢʔβʔΤϥʔϝοηʔδͷ༰Λ֬ೝͯ͠ଈ࠲ʹೖྗΛमਖ਼Ͱ͖Δ 3
ϑΥʔϜόϦσʔγϣϯͷ࣮ݱ w ϑΥʔϜͷ֤ϑΟʔϧυͷೖྗΛͦΕͧΕνΣοΫ w ֤ϑΟʔϧυʹରͯ͠όϦσʔγϣϯϧʔϧΛఆٛͯ͠ద༻ w ֤ϑΟʔϧυͷঢ়ଶ ݕূ݁ՌͳͲ ͷཧ͕ඞཁ w
Τϥʔ͕͋ΔϑΟʔϧυʹରͯ͠ΤϥʔϝοηʔδΛదʹදࣔ w ϑΟʔϧυͷݕূ݁Ռʹج͍ͮͯΤϥʔͷදࣔɾඇදࣔͷঢ়ଶͷཧ͕ඞཁ w ϑΥʔϜͷཧϥΠϒϥϦΛར༻͢Δ͜ͱͰ࣮Λ؆ૉԽ w ϑΥʔϜͷঢ়ଶཧ w όϦσʔγϣϯͱΤϥʔϝοηʔδͷද੍ࣔޚ 4
3FBDU)PPL'PSNͷ֓ཁ
3FBDU)PPL'PSNͱ w 3FBDUͰϑΥʔϜΛ؆୯ʹѻ͑ΔΑ͏ʹ͢ΔϥΠϒϥϦ w 3FBDUͷ)PPLTʹΑͬͯϑΥʔϜͷঢ়ଶཧόϦσʔγϣϯ͕Մೳ w ϑΥʔϜͷೖྗঢ়ଶͷอ࣋ w όϦσʔγϣϯͱΤϥʔϝοηʔδͷදࣔ w
ϑΥʔϜͷૹ৴ॲཧ 6
3FBDU)PPL'PSNͷ͍ํ w ϑΥʔϜϑΟʔϧυΛ3FBDU)PPL'PSNͰཧରͱͯ͠ొ w ϑΥʔϜૹ৴࣌ͷॲཧΛࢦఆ w ϑΥʔϜͷόϦσʔγϣϯͱΤϥʔͷදࣔ 7
ϑΥʔϜσʔλͷܕఆٛ w ϑΥʔϜͰѻ͏σʔλͷܕΛఆٛ w ఆٛͨ͠ܕΛuseForm()ͷܕҾʹࢦఆՄೳ w ϑΥʔϜϑΟʔϧυͷͷܕࢦఆ͕ՄೳʹͳΔ 8 import {
useForm } from 'react-hook-form'; interface FormInput { name: string; email: string; age: number; } useForm<FormInput>();
ϑΥʔϜϑΟʔϧυͷొ w VTF'PSNIPPL͔Βregister()Λऔಘ w ϑΥʔϜϑΟʔϧυΛ3FBDU)PPL'PSNͷཧରʹొ w register()ͷҾʹೖྗϑΥʔϜʹࢦఆ͢ΔҰҙͳ໊લΛࢦఆ w useForm()ͰܕҾΛࢦఆͨ͠߹ͦͷܕͷϓϩύςΟ໊ͷΈʹ੍͞ΕΔ w
ϑΥʔϜཁૉͰregister()Λ༻ 9 const { register } = useForm<FormInput>(); return ( <form> <input {...register('name')} /> <!-- 略 --> </form> );
ϑΥʔϜͷૹ৴ॲཧ w VTF'PSNIPPL͔ΒhandleSubmit()Λऔಘ w formཁૉͷonSubmitʹhandleSubmit()Λࢦఆ w handleSubmit()ͷҾʹϑΥʔϜૹ৴࣌ʹ࣮ߦ͢ΔؔΛࢦఆ 10 const {
register, handleSubmit } = useForm(); const onSubmit = (data: FormInput) => { // 送信時の処理 }; return ( <form onSubmit={handleSubmit(onSubmit)}> <!-- 略 --> </form> );
ϑΥʔϜͷόϦσʔγϣϯ w register()ͷୈҾʹόϦσʔγϣϯϧʔϧΛࢦఆ w ඞਢ࠷খ࠷େͳͲΛࢦఆՄೳ w ΤϥʔϝοηʔδͷઃఆՄೳ 11 const {
register } = useForm<FormInput>(); return ( <form> <input {...register('name', { required: { value: true, message: '必須項目です', } })} /> <!-- 略 --> </form> );
Τϥʔϝοηʔδͷදࣔ w VTF'PSNIPPL͔ΒformStateΛऔಘͯ͠errorsΛऔΓग़͢ w errorsʹͯ͢ͷϑΥʔϜϑΟʔϧυͷΤϥʔΛแؚ w register()Ͱࢦఆͨ͠ϑΟʔϧυ໊ͱerrorsͷϓϩύςΟ໊͕ରԠ 12 const {
/* 略 */ formState: { errors } } = useForm(); return ( <form> <input {...register('name', { /* 略 */ }) /> {errors.name && (<span>{errors.name.message}</span>)} </form> );
;PEͷ֓ཁ
;PEͱ w 5ZQF4DSJQU fi STUͳόϦσʔγϣϯϥΠϒϥϦ w ܕʹج͍ͮͯεΩʔϚΛఆٛ w จࣈྻͷΑ͏ͳ୯७ͳͷ͔Βෳࡶͳߏ·ͰॊೈʹఆٛՄೳ w
ఆٛͨ͠εΩʔϚʹج͍ͮͯೖྗΛݕূ w ಛఆͷϥΠϒϥϦϑϨʔϜϫʔΫʹґଘ͠ͳ͍ w 3FBDU)PPL'PSNҎ֎ͷϑΥʔϜϥΠϒϥϦʹར༻Մೳ w 7VFͳͲͷ3FBDUҎ֎ͷϑϨʔϜϫʔΫͰར༻Մೳ 14
;PEͷ͍ํ w ;PEͰόϦσʔγϣϯ͢ΔͨΊͷεΩʔϚΛఆٛ w όϦσʔγϣϯϧʔϧͳͲ߹Θͤͯఆٛ w ఆٛͨ͠εΩʔϚΛར༻ͯ͠ೖྗΛݕূ w Τϥʔϝοηʔδͷઃఆ 15
εΩʔϚͷఆٛ w ;PEͷϝιουΛར༻ͯ͠εΩʔϚͷܕΛఆٛ w string()number()ͳͲͰجຊܕΛදݱ w object()ͰΦϒδΣΫτΛදݱ w ҾͷΦϒδΣΫτͰ͞Βʹ;PEͷεΩʔϚͷܕఆ͕ٛՄೳ w
ೖΕࢠߏͷεΩʔϚͷఆٛՄೳ w array()ͰྻΛදݱ 16 import * as z from 'zod'; const UserSchema = z.object({ name: z.string(), email: z.email(), age: z.number(), });
ೖྗͷݕূ w parse()Λར༻ͯ͠εΩʔϚͷఆ͔ٛΒೖྗΛݕূ w όϦσʔγϣϯΤϥʔ͕͋Δ߹ZodError͕UISPX͞ΕΔ w safeParse()Λར༻ͯ͠ݕূ݁ՌͷΦϒδΣΫτΛऔಘ w safeParse()ͰZodErrorUISPX͞Εͳ͍ w
ݕূ݁ՌͷΦϒδΣΫτΛฦ٫ w ฦ٫ܕݕূޭɾࣦഊͷ݁ՌͷVOJPOܕ 17 try { UserSchema.parse({ name: 'foo', age: 31 }); } catch (error) { if (error instanceof z.ZodError) { // エラー処理 } }
Τϥʔϝοηʔδͷઃఆ w εΩʔϚఆٛͷؔͷҾͰΤϥʔϝοηʔδΛࢦఆՄೳ w ZodErrorͷmessageʹࢦఆͨ͠Τϥʔϝοηʔδ͕ઃఆ͞ΕΔ 18 z.email('Invalid email address'); z.number().positive('value
should be positive');
3FBDU)PPL'PSN ;PEͷ όϦσʔγϣϯ
3FBDU)PPL'PSNͱ;PEΛΈ߹ΘͤΔ w @hookform/resolversΛΠϯετʔϧ w useForm()ͷresolverʹzodResolverΛࢦఆ w @hookform/resolvers/zod͔ΒJNQPSU w useForm()ͷܕҾʹ;PEͷεΩʔϚ͔Βੜ͞ΕΔܕΛࢦఆ w
ϑΥʔϜσʔλͷܕΛ;PEͷεΩʔϚఆٛͱ࿈ಈͤ͞Δ 20
εΩʔϚఆ͔ٛΒܕΛੜ w ;PEͷinfer<>ͰεΩʔϚఆ͔ٛΒਪ͞ΕͨܕΛੜ w infer<>ͷܕҾʹ;PEͷεΩʔϚఆٛͷܕใΛࢦఆ w typeofԋࢉࢠͰܕใΛऔಘ w ੜ͞ΕͨܕϑΥʔϜͷೖྗܕͱͯ͠ར༻Մೳ 21
const UserSchema = z.object({ name: z.string(), email: z.email(), age: z.number(), }); type UserType = z.infer<typeof UserSchema>;
3FBDU)PPL'PSNʹ;PEΛઃఆ w useForm()ͷΦϓγϣϯͷresolverʹzodResolver()Λࢦఆ w zodResolver()ͷҾʹ;PEͰఆٛͨ͠εΩʔϚΦϒδΣΫτΛࢦఆ w useForm()ͷܕҾʹ;PEͰੜͨ͠ܕΛࢦఆ w register()ͷόϦσʔγϣϯϧʔϧͷઃఆෆཁ w
;PEͷεΩʔϚఆٛʹج͍ͮͯࣗಈతʹόϦσʔγϣϯ͞ΕΔ 22 import { zodResolver } from '@hookform/resolvers/zod'; const { /* 略 */ } = useForm<UserType>({ resolver: zodResolver(UserSchema), });
/FYUKTͰར༻͢Δࡍͷҙ w 3FBDU)PPL'PSNΫϥΠΞϯταΠυͰͷΈಈ࡞ w 3FBDU)PPL'PSNΛར༻͢Δίϯϙʔωϯτʹ'use client'͕ඞཁ w αʔόʔίϯϙʔωϯτͰར༻͠Α͏ͱ͢ΔͱϏϧυΤϥʔ͕ൃੜ 23 'use
client'; import { useForm } from "react-hook-form";
·ͱΊ w 3FBDU)PPL'PSNͷجૅతͳ͍ํʹ͍ͭͯઆ໌ w ;PEʹ͍ͭͯͷجૅతͳ͍ํʹ͍ͭͯઆ໌ w 3FBDU)PPL'PSNͱ;PEΛΈ߹Θͤͨར༻ํ๏ʹ͍ͭͯઆ໌ 24
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠
એ w ϑϩϯτΤϯυΧϯϑΝϨϯεؔ։࠵༧ఆ w w ॴϚΠυʔϜ͓͓͔͞' w
ҰൠࢀՃνέοτ ·͍Ͳνέοτ ൃചத