Formik注册功能异常求助:Firestore未写入、页面未跳转
问题排查与解决
问题概述
使用React、TypeScript、Formik和Firebase开发学习项目,Firebase认证可正常创建用户,但存在以下问题:
- 页面无法跳转至
/main路由 - 表单提交后未自动清空
- 用户数据未写入Firestore
代码片段(Registration.tsx)
import React from "react"; import { Formik, Form, Field, ErrorMessage, FormikHelpers } from "formik"; import * as Yup from "yup"; // Firebase import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; import { collection, getFirestore } from "firebase/firestore"; import firebase from "firebase/compat/app"; import "firebase/compat/auth"; import "firebase/compat/firestore"; // Navigate import { useNavigate } from "react-router-dom"; // Initialize Firebase const app = firebase.initializeApp(firebaseConfig); const auth = getAuth(app); const firestore = getFirestore(app); const Registration: React.FC = () => { const navigate = useNavigate(); const handleRedirect = () => { navigate("/main"); }; const initialValues = { name: "", email: "", password: "", confirmPassword: "", }; const validationSchema = Yup.object({ name: Yup.string().required("Name is required"), email: Yup.string().email("Invalid email").required("Email is required"), password: Yup.string() .required("Password is required") .min(8, "Password must be at least 8 characters") .matches( /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/, "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character" ), confirmPassword: Yup.string() .test("password-match", "Passwords must match", function (value) { return value === this.resolve(Yup.ref("password")); }) .nullable() .required("Confirm Password is required"), }); const registrationSubmit = async ( values: typeof initialValues, { resetForm }: FormikHelpers<typeof initialValues> ) => { try { const { name, email, password } = values; if (name && email && password) { const userCredential = await firebase .auth() .createUserWithEmailAndPassword(email, password); await userCredential.user?.updateProfile({ displayName: name }); try { console.log(userCredential.user?.uid); const db = firebase.firestore(); await db.collection("users").doc(userCredential.user?.uid).set({ name, email, password, }); console.log("Registration was successful"); resetForm(); navigate("/main"); } catch (error) { console.error("Error writing to Firestore:", error); } } else { console.log("Please provide all required fields"); } } catch (error) { console.log(error); } }; return ( <div className="page bg-teal-50"> <div className="container flex flex-col justify-center align-center"> <h1 className="text-5xl font-bold text-center text-gray-800"> Registration </h1> <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={registrationSubmit} > {({ errors, touched }) => ( <Form className="flex flex-col m-auto" autoComplete="off"> <div className="flex mt-16 items-center justify-center flex-col "> <Field type="text" id="name" name="name" placeholder="Username" className={`w-64 h-12 text-gray-900 pl-4 outline-none border-solid border-2 rounded focus:border-teal-600 ${ errors.name && touched.name ? "border-red-500" : "border-teal-400" }`} />{" "} <ErrorMessage name="name" component="p" className="text-red-600 font-bold text-lg flex align-center mt-4" /> </div> <div className="flex mt-4 items-center justify-center flex-col "> <Field type="email" id="email" name="email" placeholder="Email" className={`w-64 h-12 text-gray-900 pl-4 outline-none border-solid border-2 rounded focus:border-teal-600 ${ errors.name && touched.name ? "border-red-500" : "border-teal-400" }`} /> <ErrorMessage name="email" component="p" className="text-red-600 font-bold text-lg flex align-center mt-4" /> </div> <div className="flex mt-4 items-center justify-center flex-col "> <Field type="password" id="password" name="password" placeholder="Password" className={`w-64 h-12 text-gray-900 pl-4 outline-none border-solid border-2 rounded focus:border-teal-600 ${ errors.name && touched.name ? "border-red-500" : "border-teal-400" }`} /> <ErrorMessage name="password" component="p" className="text-red-600 font-bold text-lg flex align-center mt-4" /> </div> <div className="flex mt-4 items-center justify-center flex-col "> <Field type="password" id="confirmPassword" name="confirmPassword" placeholder="Confirm Password" className={`w-64 h-12 text-gray-900 pl-4 outline-none border-solid border-2 rounded focus:border-teal-600 ${ errors.name && touched.name ? "border-red-500" : "border-teal-400" }`} /> <ErrorMessage name="confirmPassword" component="p" className="text-red-600 font-bold text-lg flex align-center mt-4" /> </div> <button type="submit" className="bg-blue-500 w-32 h-12 cursor-pointer mt-8 m-auto text-white text-xl rounded hover:bg-blue-700 transition-colors duration-300" > Register </button> </Form> )} </Formik> </div> </div> ); }; export default Registration;
问题排查与修复方案
1. Firestore写入失败的核心原因
(1)安全规则限制
Firebase默认的Firestore安全规则禁止未认证用户的读写操作,即使已创建用户,若规则未配置,写入会被拒绝。需修改Firestore安全规则为:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow create, read, update, delete: if request.auth != null && request.auth.uid == userId; } } }
此规则仅允许认证用户写入自己的文档,确保数据安全。
(2)用户UID为空判断缺失
代码中userCredential.user?.uid存在为空的可能,导致写入文档的ID为undefined触发错误。需添加非空判断:
if (userCredential.user) { await db.collection("users").doc(userCredential.user.uid).set({ name, email, // 重要:不要存储明文密码!Firebase Auth会自动管理密码,无需额外存储 // password, }); }
(3)Firebase实例混用
代码同时导入了Firebase模块化API和兼容版API,可能导致实例冲突。建议统一使用兼容版:
// 移除模块化API导入 // import { initializeApp } from "firebase/app"; // import { getAuth } from "firebase/auth"; // import { collection, getFirestore } from "firebase/firestore"; // 保留兼容版导入 import firebase from "firebase/compat/app"; import "firebase/compat/auth"; import "firebase/compat/firestore"; // 初始化 const app = firebase.initializeApp(firebaseConfig); const auth = firebase.auth(app); const firestore = firebase.firestore(app);
2. 页面不跳转、表单不清空的原因
这两个问题是Firestore写入失败的连锁反应:代码中resetForm()和navigate("/main")仅在Firestore写入成功后执行,一旦写入失败进入catch块,这两个操作就不会触发。修复Firestore写入问题后,这两个功能会自动恢复。
3. 额外UI问题(非核心但需修复)
所有Field的错误样式判断都绑定了errors.name && touched.name,导致email、password等字段的错误状态与name字段关联,需修改为对应字段的判断:
比如email字段的className应改为:
className={`w-64 h-12 text-gray-900 pl-4 outline-none border-solid border-2 rounded focus:border-teal-600 ${ errors.email && touched.email ? "border-red-500" : "border-teal-400" }`}
password和confirmPassword字段同理,替换为各自的errors.password、errors.confirmPassword。
内容的提问来源于stack exchange,提问作者Ruslan Makhmatov
相关产品推荐
相关产品推荐

