Home > OS >  Cannot read properties of undefined (reading 'indexOf') with Firebase v9 Modular React JS
Cannot read properties of undefined (reading 'indexOf') with Firebase v9 Modular React JS

Time:09-16

I got this simple form that registers users and send the uid and email data to a collection on firestore, im using latest version of Firebase 9 w/ modular. The authentication works great, but the firestore part doesnt. It throws me an error:

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

I dont know what does that mean, I am trying to upload the email and uid information of the registered user to firestore with the order of 'users / (and here the useruid)/ all the user data' but throws me that error.

I leave my code:

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

CodePudding user response:

Issue

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

userData.uid is likely the culprit since userData is still the initial empty string ('') state value. Remember, React state updates are asynchronously processed, so the setUserData(user) won't have updated the userData state yet when it's referenced a few lines below in collection(db, 'users', userData.uid).

Solution

I suggest splitting out the second half of the registro function into an useEffect hook with a dependency on the userData state to issue the side-effect of adding the document.

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 (
    ...
  )
}

CodePudding user response:

You don't necessary have to read data from userData state while adding a document. Try passing the params directly from user object returned:

try {
  const { user } = await createUserWithEmailAndPassword(auth, email, pass)
  setUserData(user)
  
  await setDoc(doc(db, 'users', user.uid), {
  //    ^^^    ^^^<-- DocumentReference and not CollectionReference
    email: user.email,
    uid: user.uid
  })
                
} catch (e) {
  console.log(e)
}

Also you were passing 2 path segments in collection() which might return an error since a CollectionReference path takes odd number of path segments. If you want user's UID as document ID then use setDoc since addDoc will generate a random ID.

  • Related