Skip to content

Commit 6e8d274

Browse files
committed
react-hook-form を導入
1 parent 9088fbd commit 6e8d274

File tree

3 files changed

+131
-128
lines changed

3 files changed

+131
-128
lines changed
Lines changed: 129 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,148 @@
1-
import { type ChangeEvent, useState } from "react";
1+
import { useEffect } from "react";
22

33
import type { StepProps } from "~/app/signup/common";
44
import { facultiesAndDepartments } from "~/app/signup/data";
5-
import { parseStep1UserSchema } from "~/common/zod/methods";
65
import type { Step1User } from "~/common/zod/types";
76

8-
function Label({ children }: { children: string }) {
9-
return <span className="text-gray-500 text-sm">{children}</span>;
10-
}
7+
import { zodResolver } from "@hookform/resolvers/zod";
8+
import { type FieldError, type SubmitHandler, useForm } from "react-hook-form";
9+
import { Step1UserSchema } from "~/common/zod/schemas";
1110

1211
const faculties = Object.keys(facultiesAndDepartments);
1312
export default function Step1({ onSave, prev, caller }: StepProps<Step1User>) {
14-
const [name, setName] = useState(prev?.name ?? "");
15-
const [gender, setGender] = useState(prev?.gender ?? "その他");
16-
const [grade, setGrade] = useState(prev?.grade ?? "");
17-
const [faculty, setFaculty] = useState(prev?.faculty ?? "");
18-
const [department, setDepartment] = useState(prev?.department ?? "");
19-
const [intro, setIntro] = useState(prev?.intro ?? "");
20-
const [errorMessage, setErrorMessage] = useState("");
21-
22-
async function save() {
23-
try {
24-
const data: Step1User = {
25-
name: name.trim(),
26-
grade: grade,
27-
gender: gender,
28-
faculty: faculty,
29-
department: department,
30-
intro: intro.trim(),
31-
};
32-
parseStep1UserSchema(data);
33-
onSave(data);
34-
} catch (error) {
35-
if (error instanceof Error) {
36-
let errorMessages: string;
37-
try {
38-
const parsedError = JSON.parse(error.message);
39-
if (Array.isArray(parsedError)) {
40-
errorMessages = parsedError.map((err) => err.message).join(", ");
41-
} else {
42-
errorMessages = error.message;
43-
}
44-
} catch {
45-
errorMessages = error.message;
46-
}
47-
48-
// エラーメッセージをセット
49-
setErrorMessage(errorMessages);
50-
} else {
51-
console.log("unknown error:", error);
52-
setErrorMessage("入力に誤りがあります。");
53-
}
54-
}
55-
}
56-
57-
const handleFacultyChange = (event: ChangeEvent<HTMLSelectElement>) => {
58-
setFaculty(event.target.value);
59-
};
60-
61-
const handleDepartmentChange = (event: ChangeEvent<HTMLSelectElement>) => {
62-
setDepartment(event.target.value);
13+
const {
14+
register,
15+
handleSubmit,
16+
watch,
17+
setValue,
18+
resetField,
19+
formState: { errors },
20+
} = useForm<Step1User>({
21+
resolver: zodResolver(Step1UserSchema),
22+
defaultValues: prev,
23+
});
24+
const onSubmit: SubmitHandler<Step1User> = async (data) => {
25+
onSave(data);
6326
};
27+
const selectedFaculty = watch("faculty");
6428

29+
useEffect(() => {
30+
if (selectedFaculty) {
31+
const defaultDepartment = facultiesAndDepartments[selectedFaculty][0];
32+
setValue("department", defaultDepartment);
33+
} else {
34+
resetField("department");
35+
}
36+
}, [selectedFaculty, setValue, resetField]);
6537
return (
6638
<>
67-
<div className="m-4 mb-8 flex flex-col gap-4 ">
39+
<div className="m-4 mb-8 flex flex-col gap-4">
6840
<h1 className="text-xl">アカウント設定</h1>
6941
<div className="flex flex-col gap-2">
70-
<Label>名前</Label>
71-
<input
72-
className="input input-bordered w-full"
73-
value={name}
74-
onChange={(e) => setName(e.target.value)}
75-
autoComplete="off"
76-
/>
77-
<Label>性別</Label>
78-
<select
79-
className="select select-bordered w-full"
80-
value={gender}
81-
onChange={(e) => setGender(e.target.value)}
82-
>
83-
<option value={"男性"}>男性</option>
84-
<option value={"女性"}>女性</option>
85-
<option value={"その他"}>その他</option>
86-
<option value={"秘密"}>秘密</option>
87-
</select>
88-
<Label>学年</Label>
89-
<select
90-
className="select select-bordered w-full"
91-
value={grade}
92-
onChange={(e) => setGrade(e.target.value)}
93-
>
94-
<option value={"B1"}>1年生 (B1)</option>
95-
<option value={"B2"}>2年生 (B2)</option>
96-
<option value={"B3"}>3年生 (B3)</option>
97-
<option value={"B4"}>4年生 (B4)</option>
98-
<option value={"M1"}>修士1年 (M1)</option>
99-
<option value={"M2"}>修士2年 (M2)</option>
100-
</select>
101-
<Label>学部</Label>
102-
<select
103-
className="select select-bordered w-full"
104-
value={faculty}
105-
onChange={handleFacultyChange}
106-
>
107-
{faculties.map((fac) => (
108-
<option key={fac} value={fac}>
109-
{fac}
110-
</option>
111-
))}
112-
</select>
113-
<Label>学科 (先に学部を選択して下さい)</Label>
114-
<select
115-
className="select select-bordered w-full"
116-
value={department}
117-
onChange={handleDepartmentChange}
118-
disabled={!faculty}
119-
>
120-
{faculty &&
121-
facultiesAndDepartments[faculty].map((dep) => (
122-
<option key={dep} value={dep}>
123-
{dep}
124-
</option>
125-
))}
126-
</select>
127-
<Label>自己紹介</Label>
128-
<textarea
129-
className="textarea textarea-bordered w-full"
130-
rows={5}
131-
placeholder="こんにちは!仲良くして下さい!"
132-
onChange={(e) => setIntro(e.target.value)}
133-
/>
134-
{errorMessage && (
135-
<div className="mb-4 text-error">{errorMessage}</div>
136-
)}
42+
<form onSubmit={handleSubmit(onSubmit)}>
43+
<Field fieldName="name" fieldLabel="名前" error={errors?.name}>
44+
<input
45+
className="input input-bordered w-full"
46+
{...register("name")}
47+
/>
48+
</Field>
49+
<Field fieldName="gender" fieldLabel="性別" error={errors?.gender}>
50+
<select
51+
className="select select-bordered w-full"
52+
{...register("gender")}
53+
>
54+
<option value={"男性"}>男性</option>
55+
<option value={"女性"}>女性</option>
56+
<option value={"その他"}>その他</option>
57+
<option value={"秘密"}>秘密</option>
58+
</select>
59+
</Field>
60+
<Field fieldName="grade" fieldLabel="学年" error={errors?.grade}>
61+
<select
62+
className="select select-bordered w-full"
63+
{...register("grade")}
64+
>
65+
<option value={"B1"}>1年生 (B1)</option>
66+
<option value={"B2"}>2年生 (B2)</option>
67+
<option value={"B3"}>3年生 (B3)</option>
68+
<option value={"B4"}>4年生 (B4)</option>
69+
<option value={"M1"}>修士1年 (M1)</option>
70+
<option value={"M2"}>修士2年 (M2)</option>
71+
</select>
72+
</Field>
73+
<Field
74+
fieldName="faculty"
75+
fieldLabel="学部"
76+
error={errors?.faculty}
77+
>
78+
<select
79+
className="select select-bordered w-full"
80+
{...register("faculty")}
81+
>
82+
{faculties.map((fac) => (
83+
<option key={fac} value={fac}>
84+
{fac}
85+
</option>
86+
))}
87+
</select>
88+
</Field>
89+
<Field
90+
fieldName="department"
91+
fieldLabel="学科 (先に学部を選択して下さい)"
92+
error={errors?.department}
93+
>
94+
<select
95+
className="select select-bordered w-full"
96+
{...register("department")}
97+
disabled={!selectedFaculty}
98+
>
99+
{selectedFaculty &&
100+
facultiesAndDepartments[selectedFaculty].map((dep) => (
101+
<option key={dep} value={dep}>
102+
{dep}
103+
</option>
104+
))}
105+
</select>
106+
</Field>
107+
<Field
108+
fieldName="intro"
109+
fieldLabel="自己紹介"
110+
error={errors?.intro}
111+
>
112+
<textarea
113+
className="textarea textarea-bordered w-full"
114+
rows={5}
115+
placeholder="こんにちは!仲良くして下さい!"
116+
{...register("intro")}
117+
/>
118+
</Field>
119+
<div className="flex justify-end">
120+
<button type="submit" className="btn btn-primary">
121+
{caller === "registration" ? "次へ" : "保存"}
122+
</button>
123+
</div>
124+
</form>
137125
</div>
138126
</div>
139-
<div className="fixed bottom-5 flex w-full justify-between p-6">
140-
<span />
141-
<button type="button" onClick={save} className="btn btn-primary">
142-
{caller === "registration" ? "次へ" : "保存"}
143-
</button>
144-
</div>
145127
</>
146128
);
147129
}
130+
131+
function Field({
132+
fieldLabel,
133+
children,
134+
error,
135+
}: {
136+
fieldName: string;
137+
fieldLabel: string;
138+
children: React.ReactNode;
139+
error: FieldError | undefined;
140+
}) {
141+
return (
142+
<div className="my-2">
143+
<div className="text-gray-500 text-sm">{fieldLabel}</div>
144+
{children}
145+
<div className="text-error text-sm">{error?.message}</div>
146+
</div>
147+
);
148+
}

web/bun.lockb

833 Bytes
Binary file not shown.

web/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@emotion/react": "^11.11.4",
1313
"@emotion/styled": "^11.11.5",
1414
"@fontsource/roboto": "^5.0.13",
15+
"@hookform/resolvers": "^3.9.1",
1516
"@mui/icons-material": "^5.16.7",
1617
"@mui/material": "^5.15.20",
1718
"devalue": "^5.1.1",
@@ -22,6 +23,7 @@
2223
"react": "^18.2.0",
2324
"react-dom": "^18.2.0",
2425
"react-easy-crop": "^5.0.8",
26+
"react-hook-form": "^7.53.2",
2527
"react-icons": "^5.3.0",
2628
"socket.io-client": "^4.7.5",
2729
"swiper": "^11.1.14",

0 commit comments

Comments
 (0)