开发者问题收集

无法使用 Firebase v9 Modular React JS 读取未定义的属性(读取'indexOf')

2021-09-16
14022

我得到了这个简单的表单,用于注册用户并将 uid 和电子邮件数据发送到 firestore 上的集合,我使用的是最新版本的 Firebase 9 w/ 模块化。 身份验证工作正常,但 firestore 部分不行。它抛出一个错误:

TypeError: Cannot read properties of undefined (reading 'indexOf')
at Function.fromString (path.ts:229)
at Ac2 (reference.ts:374)
at Login.jsx:29

我不知道那是什么意思,我试图将注册用户的电子邮件和 uid 信息上传到 firestore,顺序为“用户/(这里是用户 uid)/所有用户数据”,但抛出了那个错误。

我留下我的代码:

import React, { useState, useEffect, useCallback } from 'react'
import { auth, db } from "../firebase"
import { createUserWithEmailAndPassword } from "firebase/auth";
import { collection, doc, addDoc, setDoc } from "firebase/firestore"; 



function Login() {
    const [email, setEmail] = useState('')
    const [pass, setPass] = useState('')
    const [error, setError] = useState('')
    const [userData, setUserData] = useState('')

    const ProcessData = e => {
        e.preventDefault()
        registro()
    }

    const registro = useCallback(async() => {

        try {
            await createUserWithEmailAndPassword(auth, email, pass)
            .then((userCredential) => {
                const user = userCredential.user;
                setUserData(user)
            })
            
            
            await addDoc(collection(db, 'users', userData.uid), {
                email: userData.email,
                uid: userData.uid
              })
                
            
            
            setEmail('')
            setPass('')
            setError('')

        } catch (error) {
            const errorCode = error.code;
            const errorMessage = error.message;

            setError(errorMessage)
            console.log(error)
        }
            
    }, [email, pass, error, userData])

    return (
        <div>
            <h3>Registro</h3>
            <form onSubmit={ProcessData}>
                <label>Email Adress</label>
                <input
                type="email"
                placeholder="Email Address"
                onChange={e => setEmail(e.target.value)}
                value={email}
                />
                <label>Password</label>
                <input
                type="password"
                placeholder="Password"
                onChange={e => setPass(e.target.value)}
                value={pass}
                />
                <button type="submit">Registrarse</button>
            </form>
        </div>
    )
}

export default Login
2个回答

添加文档时,您不一定需要从 userData 状态读取数据。尝试直接从返回的 user 对象传递参数,这样 doc() 中的任何路径段都不会未定义:

try {
  const { user } = await createUserWithEmailAndPassword(auth, email, pass)
  setUserData(user)
  
  // db must not be undefined
  // try console.log(db) to ensure it's a Firestore instance
  await setDoc(doc(db, 'users', user.uid), {
  //    ^^^    ^^^<-- DocumentReference and not CollectionReference
    email: user.email,
    uid: user.uid
  })
                
} catch (e) {
  console.log(e)
}

此外,您在 collection() 中传递了 2 个路径段,这可能会返回错误,因为 CollectionReference 路径采用奇数个路径段。如果您希望将用户的 UID 作为文档 ID,请使用 setDoc ,因为 addDoc 将生成一个随机 ID。

Dharmaraj
2021-09-16

问题

Line 29 of Login: await addDoc(collection(db, 'users', userData.uid), {

userData.uid 可能是罪魁祸首,因为 userData 仍然是初始空字符串 ( '' ) 状态值。请记住,React 状态更新是异步处理的,因此当 setUserData(user)collection(db, 'users', userData.uid) 中引用以下几行时,它尚未更新 userData 状态。

解决方案

我建议将 registro 函数的后半部分拆分为一个 useEffect 钩子,并依赖于 userData 状态以发出添加文档的副作用。

function Login() {
  const [email, setEmail] = useState('');
  const [pass, setPass] = useState('');
  const [error, setError] = useState('');
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    if (userData) {
      const { email, uid } = userData;
      try {
        addDoc(collection(db, 'users', uid), { email, uid });
      } catch (error) {
        const { code, message } = error;

        setError(message);
        console.log(error);
      }
    }

  }, [userData]);

  const ProcessData = e => {
    e.preventDefault();
    registro();
  }

  const registro = useCallback(async() => {
    try {
      await createUserWithEmailAndPassword(auth, email, pass)
        .then((userCredential) => {
          const { user } = userCredential;
          setUserData(user);
        });
            
      setEmail('');
      setPass('');
      setError('');

    } catch (error) {
      const { code, message } = error;

      setError(message);
      console.log(error);
    }
            
  }, [email, pass, error, userData])

  return (
    ...
  )
}
Drew Reese
2021-09-16