为模拟面试平台中的用户身份验证模块,实现注册 / 登录功能。使用技术栈包括:
Next.js 13
+App Router
TypeScript
React Hook Form
Zod
用于 schema 校验Sonner
实现toast
提示- 自定义组件如
FormField
,Button
,Form
该项目将作为简历中的展示项目,目标是:代码清晰、逻辑完整、类型安全、可讲解。
🧰 核心功能模块划分
1. 表单 Schema 生成 (authFormSchema)
- 利用
Zod
按照身份类型动态生成 schema - Sign-Up 需要
name
字段,Sign-In 不需要
css
const authFormSchema = (type: FormType) =>
z.object({
name: type === "sign-up" ? z.string().min(3) : z.string().optional(),
email: z.string().email(),
password: z.string().min(3),
});
2. 表单形成 (React Hook Form + zodResolver)
useForm
和 schema 绑定- 用 TS 定义表单值类型
defaultValues
确保 RHF 初始化正确
css
const formSchema = authFormSchema(type);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
email: "",
password: "",
},
});
3. 提交函数 onSubmit
- 根据 type 判断是 sign-up 还是 sign-in
- 使用
router.push
进行跳转 - 给与用户不同 toast feedback
vbnet
function onSubmit(values: z.infer<typeof formSchema>) {
try {
if (type === "sign-up") {
toast.success("Account created successfully. Please sign in.");
router.push("/sign-in");
} else {
toast.success("Sign in successfully.");
router.push("/");
}
} catch (error) {
toast.error(`There was an error: ${error}`);
}
}
4. 表单字段组件 FormField(✨设计亮点)
-
自定义封装组件
FormField
用于统一表单字段逻辑:- 自动绑定 RHF 的
control
- 封装
label
+input
渲染逻辑 - 支持通用 props(如
placeholder
,type
)
- 自动绑定 RHF 的
ini
<FormField
control={form.control}
name="email"
label="Email"
placeholder="Your email address"
type="email"
/>
🔹 价值非常大:
- 减少重复代码
- 增强表单复用性与可维护性
- 一致化 UI 和行为逻辑
⚡️ Debug 重点回顾 (TypeScript + RHF + Zod)
❌ 1. z.infer<typeof formSchema>
使用时报错
问题原因 : formSchema
在函数体内部才声明,导致接口型别无法推断
解决方案:
ini
// 尽量接在 formSchema 之后声明类型
const formSchema = authFormSchema(type);
type AuthFormValues = z.infer<typeof formSchema>;
❌ 2. ReturnType<typeof authFormSchema>
无法 infer
问题原因 : 没有给 authFormSchema
指定参数,结果是 infer 一个函数,而不是 schema
解决方案: 利用实际执行后的 schema 进行 infer
ini
const schema = authFormSchema("sign-in");
type AuthFormValues = z.infer<typeof schema>;
❌ 3. FormField.tsx 路径报错
原因 : 文件名写错了,写成 FormFileld.tsx
解决: 类名和实际路径完全对应,要么改路径,要么重命名文件
javascript
// 正确写法
import FormField from "@/components/ui/FormField";
⚡️ UI 模块设计
- 利用 Tailwind CSS + flex + spacing 进行布局
- 根据
type
显示或隐藏 name 输入框 - 提供用户切换登录/注册的方便链接
✅ 总结: 我从这个小段项目中所学到的
- 完整路通 RHF + Zod 表单校验流程
- 利用 TypeScript 进行 infer 和类型分离开发
- 在实际设计中处理很多小混乱,学会了转化思路
- 从 bug 中学习自我调试,创造比照路线
- 使用
FormField
封装复用,提升开发质量
📅 接下来操作
- 联系 Firebase API 或操作本地
auth.action.ts
- 在登录成功后添加 Dashboard 控制面板模块
- 完善 Loading 状态和异步错误处理