You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firestore问题:创建用户凭据后如何同步写入信息并保证事务性?

问题分析与解决方案

错误原因

  1. UID获取失败及类型错误:报错Expected type 'th', but it was: a custom nh object通常是因为Firestore的doc函数接收的db实例初始化不正确,或是参数传递时的类型匹配问题。另外未正确定义credentials的类型,也可能导致访问user.uid时出现异常。
  2. 操作非原子性:原代码中用户账号创建成功后,若Firestore写入失败,已创建的账号不会被回滚,导致数据不一致。

修正后的代码

import { Container, Form, Button } from "react-bootstrap"
import { Link } from "react-router-dom"
import { auth, createUserWithEmailAndPassword, deleteUser, db, setDoc, doc, UserCredential } from "../../config/firebaseConfig"
import { FormEvent, useRef, useContext } from "react"
import { AuthContext } from "../../contexts/AuthContext"

export const Signup = () => {
    const emailRef = useRef<HTMLInputElement>(null)
    const passwordRef = useRef<HTMLInputElement>(null)
    const passwordConRef = useRef<HTMLInputElement>(null)
    const usernameRef = useRef<HTMLInputElement>(null)
    const user = useContext(AuthContext)
    
    async function createUserAccount(e: FormEvent) {
        e.preventDefault()

        const email = emailRef.current?.value
        const password = passwordRef.current?.value
        const passwordConfirm = passwordConRef.current?.value
        const username = usernameRef.current?.value

        if (!email || !password || !passwordConfirm || !username) {
            alert("请填写所有必填字段")
            return
        }

        if (password !== passwordConfirm) {
            alert("两次输入的密码不一致")
            return
        }

        let userCredential: UserCredential | null = null
        try {
            // 1. 创建用户账号
            userCredential = await createUserWithEmailAndPassword(auth, email, password)
            
            // 2. 写入用户信息到Firestore
            await setDoc(doc(db, "users", userCredential.user.uid), {
                name: username,
                email: email
            }) 

            alert("账号创建成功")
            // 清空表单
            emailRef.current!.value = ""
            passwordRef.current!.value = ""
            passwordConRef.current!.value = ""
            usernameRef.current!.value = ""
        } catch(e) {
            // 若Firestore写入失败,删除已创建的用户账号
            if (userCredential) {
                await deleteUser(userCredential.user)
            }
            alert(`创建失败: ${(e as Error).message}`)
        }
    }

  return (
    <Container fluid className="py-5 text-light bg-dark" id="signupContainer">
          <h2 className="text-center">Sign Up</h2>
          <Form className="w-75 mx-auto" onSubmit={createUserAccount}>
            <Form.Group>
                  <Form.Label>Username</Form.Label>
                  <Form.Control required type="text" placeholder="AwesomeUser" maxLength={20} id="signupName" ref={usernameRef}></Form.Control>
              </Form.Group>
              <Form.Group>
                  <Form.Label>Email Address</Form.Label>
                  <Form.Control required type="email" placeholder="example@address.com" id="signupEmail" ref={emailRef}></Form.Control>
              </Form.Group>
              <Form.Group>
                  <Form.Label>Password</Form.Label>
                  <Form.Control required type="password" id="signupPassword" ref={passwordRef}></Form.Control>
              </Form.Group>
              <Form.Group>
                  <Form.Label>Confirm password</Form.Label>
                  <Form.Control required type="password" id="signupPasswordConf" ref={passwordConRef}></Form.Control>
              </Form.Group>
              <Button disabled={!!user} type="submit" className="w-100 mt-5 rounded-pill">Create Account</Button>
          </Form>
          <p className="text-center text-muted">Already have an account? <Link to="/space-image-app/login">Log in</Link></p>
      </Container>
  )
}

关键修改点

  1. 类型安全:将credentials的类型从any改为Firebase官方的UserCredential,避免类型访问错误。
  2. 原子性保障:
    • 先创建用户账号,再执行Firestore写入操作
    • 若写入失败,调用deleteUser删除已创建的用户,确保两个操作要么都成功,要么都回滚
  3. 参数校验:提前获取表单值并做非空校验,避免null访问异常
  4. 错误处理优化:捕获错误时输出具体信息,同时处理回滚逻辑
  5. 代码简化:移除单独的addUserToDB函数,将逻辑合并到主函数中,减少层级便于维护

额外检查项

  • 确保firebaseConfig中db初始化正确:
    // firebaseConfig.ts示例
    import { initializeApp } from "firebase/app"
    import { getAuth } from "firebase/auth"
    import { getFirestore } from "firebase/firestore"
    
    const firebaseConfig = { /* 你的配置信息 */ }
    
    const app = initializeApp(firebaseConfig)
    export const auth = getAuth(app)
    export const db = getFirestore(app)
    
  • 确保Firestore规则允许创建用户文档(开发阶段可临时设置宽松规则,上线后需收紧):
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /users/{userId} {
          allow create: if request.auth != null && request.auth.uid == userId;
        }
      }
    }
    

内容的提问来源于stack exchange,提问作者Sean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 02:34:59