Home > Back-end >  TypeError: doc is not a function at addSubCollectionDocument
TypeError: doc is not a function at addSubCollectionDocument

Time:05-07

I'm trying to add a new document to a non-existing sub-collection in a document(solutionsCollection/docID/commentsSubCollection). But I'm getting this error message when I add a document to the sub-collection:

TypeError: doc is not a function at addSubCollectionDocument

Code to add a new document to a comments sub-collection:

  const addSubCollectionDocument = async (docID, doc) => {
    dispatch({ type: "IS_PENDING" })
    try {
      const docRef = doc(db, c, docID)
      const colRef = collection(docRef, "comments")
      const addedDocument = await addDoc(colRef, doc)
      dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
      return addedDocument
    } catch (error) {
      console.log(error)
      dispatchIfNotCancelled({ type: "ERROR", payload: error })
      return null
    }
  }

handleSubmit function:

const handleSubmit = async (e) => {
    e.preventDefault()

    try {
      const commentToAdd = {
        id: Math.floor(Math.random() * 10000),
        content: newComment.trim(),
        reactions: [],
        user: {
          userID: user.uid,
          avatarURL: user.photoURL,
          displayName: user.displayName,
          username: user.reloadUserInfo.screenName,
        },
        replies: [],
        createdAt: new Date(),
      }
      await addSubCollectionDocument(id, commentToAdd)
      setNewComment("")
      if (response) {
        console.log(response.error)
      }
    } catch (error) {
      console.log(error)
    }
  }

useFirestore hook code:

import { useEffect, useReducer, useState } from "react"
import {
  addDoc,
  collection,
  deleteDoc,
  doc,
  serverTimestamp,
  updateDoc,
} from "firebase/firestore"

import { db } from "../firebase/config"

export const useFirestore = (c) => {
  const [response, dispatch] = useReducer(firestoreReducer, initialState)
  const [isCancelled, setIsCancelled] = useState(false)

  // only dispatch is not cancelled
  const dispatchIfNotCancelled = (action) => {
    if (!isCancelled) {
      dispatch(action)
    }
  }

  // add a document
  const addDocument = async (doc) => {
    dispatch({ type: "IS_PENDING" })

    try {
      const createdAt = serverTimestamp()
      const addedDocument = await addDoc(collection(db, c), {
        ...doc,
        createdAt,
      })
      dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
    } catch (err) {
      dispatchIfNotCancelled({ type: "ERROR", payload: err.message })
    }
  }

  // Add document to sub collection
  const addSubCollectionDocument = async (docID, doc) => {
    dispatch({ type: "IS_PENDING" })
    try {
      const docRef = doc(db, c, docID)
      const colRef = collection(docRef, "comments")
      const addedDocument = await addDoc(colRef, doc)
      dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
      return addedDocument
    } catch (error) {
      console.log(error)
      dispatchIfNotCancelled({ type: "ERROR", payload: error })
      return null
    }
  }


  useEffect(() => {
    return () => setIsCancelled(true)
  }, [])

  return {
    addDocument,
    addSubCollectionDocument,
    response,
  }
}

File where I'm importing addSubCollectionDocument hook:

import { useFirestore } from "../../hooks/useFirestore"
import { useAuthContext } from "../../hooks/useAuthContext"

const SolutionComments = ({ solution }) => {
  const [newComment, setNewComment] = useState("")
  const { user } = useAuthContext()
  const { id } = useParams()
  const { addSubCollectionDocument, response } = useFirestore("solutions")

  const handleSubmit = async (e) => {
    e.preventDefault()

    try {
      const commentToAdd = {
        id: Math.floor(Math.random() * 10000),
        content: newComment.trim(),
        reactions: [],
        user: {
          userID: user.uid,
          avatarURL: user.photoURL,
          displayName: user.displayName,
          username: user.reloadUserInfo.screenName,
        },
        replies: [],
        createdAt: new Date(),
      }
      await addSubCollectionDocument(id, commentToAdd)
      setNewComment("")
      if (response) {
        console.log(response.error)
      }
    } catch (error) {
      console.log(error)
    }
  }

  return (...)}

Package.json gist: https://gist.github.com/rishipurwar1/57fbf348cd9755a940e7e3f218de570f

Firebase rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
        match /challenges/{challenge}{
        allow read: if true
    }
    match /resources/{resource}{
        allow read: if true
    }
    match /solutions/{solution}{
      allow read: if true
      allow create: if request.auth.uid != null;
      allow update, delete: if request.auth.uid == resource.data.userID;
    }
    match /users/{userId}{
        allow create: if true
      allow read: if true
    }
  }
}

CodePudding user response:

const addSubCollectionDocument = async (docID, doc) => { ... })
// That "doc" parameter is not a function      ^^^

That parameter seems to be an object that you are passing to addSubCollectionDocument(). Try renaming that to something else like:

const addSubCollectionDocument = async (docID, docData) => {
  const colRef = collection(db, c, docID, "comments")
  const addedDocument = await addDoc(colRef, doc)
})

You haven't specified security rules for your comments sub-collection. Try adding the following rules:

match /c/{docId}/comments/{commentId} {
  allow read, write: if true; 
}

Do update the if true to required rules as per your use case.

  • Related