React集成Firebase v9时创建用户同步写入Firestore出现'doc is not a function'错误及实现方案咨询
Hey there, let's fix that "doc is not a function" error and get your user data saving to Firestore properly!
What's causing the error?
The main issue is a variable name collision. In your original onSnapshot callback, you used doc as the parameter for each document in the query snapshot:
querySnapshot.docs.forEach((doc) => { ... })
But you also imported the doc function from Firebase Firestore to create document references. Inside that loop, JavaScript prioritizes the local doc variable (the document object) over the imported function—so when you tried to call doc(db, "users", username), it was trying to run a document object like a function, hence the error.
Other improvements we'll make
- Replace the inefficient "loop through all users" username check with a direct Firestore document lookup (way faster, especially as your user base grows).
- Clean up duplicate
useEffectblocks that handle alert timeouts. - Fix the missing
useStateimport (you were using it but forgot to import it from React).
Fixed Code
import React, { useState, useEffect } from "react"; // Added missing React imports import Box from "@mui/material/Box"; import OutlinedInput from "@mui/material/OutlinedInput"; import Button from "@mui/material/Button"; import Alert from "@mui/material/Alert"; import { Link, Outlet } from "react-router-dom"; import { setDoc, serverTimestamp, doc, getDoc } from "firebase/firestore"; // Removed unused imports import { createUserWithEmailAndPassword } from "firebase/auth"; import { db, auth } from "../../../firebase"; import IGLogo from "../../../images/instagram-logo.png"; import "./SignUpForm.scss"; function SignUpForm() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [username, setUsername] = useState(""); const [successalert, setSuccessAlert] = useState(undefined); const [failalert, setFailAlert] = useState(undefined); // Handle success alert timeout useEffect(() => { if (!successalert) return; const timeId = setTimeout(() => setSuccessAlert(undefined), 3000); return () => clearTimeout(timeId); }, [successalert]); // Handle failure alert timeout useEffect(() => { if (!failalert) return; const timeId = setTimeout(() => setFailAlert(undefined), 3000); return () => clearTimeout(timeId); }, [failalert]); const instagramSignUp = async (event) => { // Switched to async/await for cleaner code event.preventDefault(); try { // Step 1: Check if username already exists in Firestore const userDocRef = doc(db, "users", username); const userDocSnap = await getDoc(userDocRef); if (userDocSnap.exists()) { setFailAlert({ type: "userexist" }); return; // Exit early if username is taken } // Step 2: Create auth user if username is available const userCreated = await createUserWithEmailAndPassword(auth, email, password); // Step 3: Save user data to Firestore await setDoc(userDocRef, { uid: userCreated.user.uid, // Optional: Link auth UID to Firestore document email: email, timestamp: serverTimestamp(), }); setSuccessAlert({ type: "success" }); console.log("User created in both Auth and Firestore"); } catch (error) { console.error("Signup error:", error.message); // Add more specific error handling here if needed (e.g., invalid email format) } }; return ( <> <div className="component__signupalerts"> {successalert?.type === "success" && ( <Alert variant="filled" severity="success"> Account Created Successfully. Please check your Email for Verification. </Alert> )} {failalert?.type === "userexist" && ( <Alert variant="filled" severity="error"> Username already taken </Alert> )} </div> <div className="component__signupform"> <img src={IGLogo} alt="Instagram Logo" /> {/* Added descriptive alt text */} <Box component="form" className="component__signupform--box"> <OutlinedInput className="component__loginform--input" type="text" placeholder="Username" value={username} onChange={(e) => setUsername(e.target.value)} /> <OutlinedInput className="component__signupform--input" type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} /> <OutlinedInput className="component__signupform--input" type="password" placeholder="password" value={password} onChange={(e) => setPassword(e.target.value)} /> <Button className="component__signupform--button" type="submit" variant="contained" onClick={instagramSignUp} > Sign Up </Button> <Link to="/" className="component__signupform--button"> Sign In </Link> </Box> </div> <Outlet /> </> ); } export default SignUpForm;
Key Changes Explained
- Fixed variable collision: We removed the problematic
onSnapshotloop entirely and replaced it with a direct document lookup usinggetDoc, eliminating the name conflict with the importeddocfunction. - Async/Await syntax: Replaced nested
.then()chains with async/await to make the code easier to read and debug. - Efficient username check: Instead of loading all users, we only check the specific document ID (your username) directly—this is a single, fast read operation.
- Cleaned up alerts: Combined duplicate
useEffectblocks and added dependencies to prevent unnecessary re-runs. - Linked auth UID: Added
uid: userCreated.user.uidto the Firestore document (optional but useful for linking auth users to their Firestore data later).
内容的提问来源于stack exchange,提问作者hiimwillow
相关产品推荐
相关产品推荐

