Firebase - collection.doc is not a function - reactjs

I'm getting the following error when trying to update a document in my Firestore database.
Uncaught TypeError: machinesCollectionRef.doc is not a function
I'm reading the data just fine in another component of my React app, so I know it's not an issue with accessing the db, just probably an issue with my understanding of the documentation. Can anyone let me know where I'm going wrong?
import React, { useState } from 'react'
import {db} from'../firebase'
import {collection} from 'firebase/firestore'
export const UpdateMachine = ({machine}) => {
const [name, setName] = useState(machine.name)
const onUpdate = () => {
const machinesCollectionRef = collection(db, "Regions/Alberta/Machines")
machinesCollectionRef.doc(machine.id).update({...machine, name})
}
return (
<>
<input value={name} onChange={(e) => {setName(e.target.value)}}/>
<button onClick={onUpdate}>Update</button>
</>
)
}
EDIT: This is where I'm defining db
import firebase from 'firebase/compat/app'
import "firebase/compat/auth"
import {getFirestore} from '#firebase/firestore'
const app = firebase.initializeApp({
apiKey: "AIzaSyBhoMyfDx98mIrm_brf1Zm0MZTs7tjUTUA",
authDomain: "erg-app-dev.firebaseapp.com",
projectId: "erg-app-dev",
storageBucket: "erg-app-dev.appspot.com",
messagingSenderId: "389918287574",
appId: "1:389918287574:web:a53db3a285a8540b094b77"
})
export const db = getFirestore(app)
export const auth = app.auth()
export default app

Since you're using the new modular API doc is now a top-level function, rather than a method on a collection. The same applies to updateDoc. So:
import {collection, doc, updateDoc} from 'firebase/firestore'
export const UpdateMachine = ({machine}) => {
const [name, setName] = useState(machine.name)
const onUpdate = () => {
const machinesCollectionRef = collection(db, "Regions/Alberta/Machines")
updateDoc(doc(machinesCollectionRef, machine.id), {...machine, name});
}
...
I recommend keeping the following Firebase documentation handy:
adding and updating data
upgrading to the modular SDK.

Related

Firestore object is sometimes undefined and sometimes works fine

I am using NextJS and Firebase fairly new to both. My app is trying to collect data from firestore database on some occasions it works fine and sometimes it returns "n is undefined" refering to the firestore object "db". This happens when i call the "collection(db, "users", uid, "posts")" function in the [posts].js file. What is causing this?
Firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import {getStorage} from "firebase/storage"
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxxxxxxxxxxxxxxxxxx",
projectId: "xxxxxxxxxxxxxxxxxxxx",
storageBucket: "xxxxxxxxxxxxxxxxxxxx",
messagingSenderId: "xxxxxxxxxxxxxxxx",
appId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const storage = getStorage(app);
export const db = getFirestore(app);
[posts].js
import { useRouter } from 'next/router'
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { useAuth } from '../context/AuthContext'
import styles from '../styles/dashboard.module.css';
import Image from 'next/image';
import { collection, getDocs } from "firebase/firestore";
import { db } from '../lib/firebase';
export default function Posts(){
const [queriedData, setQueriedData] = useState([]);
const router = useRouter()
const { uid } = router.query
useEffect(() => {
async function fetchData() {
const postsRef = collection(db, "users", uid, "posts");
const snapshot = await getDocs(postsRef);
const postData = snapshot.docs.map((doc) => {
const data = doc.data();
data.id = doc.id;
return data;
});
setQueriedData(postData);
}
fetchData();
}, []);
return(
{queriedData.map((post) =>(<div>post.title</div>)
)}
}
Added await to the collection function and I also thought it was internet issues hence i setup a firebase emulator still got the same behavior.
I tried console logging "db" right after the line "export const db = getFirestore(app);". I got out "[Object Object]"

react component not getting a return from asyncronous method

I'm working on a simple application that uses firebase for google sign on authentication. The authentication part works as far as I can because I'm getting a uid for the user. However, when I try to create a firebase database instance, and call createUserDocumentFromAuth and pass it the user that was created, I don't see the app going to the method in the console-it looks like it hangs. I put a test method in-hitThis- and I get a return from it, but not the other method.
firebase.utils.js
import {initializeApp } from 'firebase/app';
import { getFirestore, collection,doc, getDocs } from 'firebase/firestore';
import {
getAuth,
signInWithRedirect,
signInWithPopup,
GoogleAuthProvider
} from 'firebase/auth';
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyB8FRK9lJ8WFJa5MnCraDBTiJWN3TJCKmg",
authDomain: "ztm-react-project.firebaseapp.com",
projectId: "ztm-react-project",
storageBucket: "ztm-react-project.appspot.com",
messagingSenderId: "737539305609",
appId: "1:737539305609:web:d7a6bd52d7af973f475658"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const provider = new GoogleAuthProvider();
provider.setCustomParameters({
prompt: "select_account"
});
export const auth = getAuth();
export const signInWithGooglePopup = () => signInWithPopup(auth,provider)
export const createUserDocumentFromAuth = async (userAuth) => {
const userDocRef = doc(db, 'users', userAuth.uid);
console.log(userDocRef);
}
export const hitThis = () =>{
console.log('hit this');
}
sign-in.component.jsx
import { signInWithGooglePopup, createUserDocumentFromAuth, hitThis} from '../../utils/firebase/firebase.utils';
const SignIn = () =>{
hitThis();
const logGoogleUser = async () => {
const user = await signInWithGooglePopup();
createUserDocumentFromAuth(user);
}
return(
<div>
<h1>Sign In Page</h1>
<button onClick={logGoogleUser}>Sign in with Google Popup</button>
</div>
);
}
export default SignIn
Your createUserDocumentFromAuth function doesn't call any API that writes data, so that'd explain why you don't see any result in the database. To actually write data, call setDoc as shown in the documentation on writing data to a new document reference.

How to sync firebase with react functional components

I would like to sync my component React with Firebase but i use a functional component i found somes answers in internet but it's seems complicated and doesn't worked for me
and i can't use this : useEffect(() => { base.syncState('/', { context: this, state: 'messages' }) });
my code is
import React,{useEffect, useState} from 'react'
import Formulaire from './component/Formulaire'
import Message from './component/Message'
import {useParams} from 'react-router-dom'
import database from './Base'
import { getDatabase, ref, set,onValue } from "firebase/database";
function App() {
const [state , setState] = useState({
messages : {},
pseudo : useParams().pseudo,
})
const addMessage = (message) =>{
state.messages[`message-${Date.now()}`] = message
setState({pseudo : state.pseudo ,messages : state.messages})
}
const msg = Object.keys(state.messages)
const lastMessages = msg.map(key=>{
return <Message key={key} pseudo={state.messages[key].pseudo} message = {state.messages[key].message} />
})
return (
<div className="container-lg col-4 mt-5">
<div>
{lastMessages}
</div>
<Formulaire length={150} addMessage ={addMessage} pseudo={state.pseudo} />
</div>
)
}
export default App
And my firebaseApp code :
import { initializeApp } from 'firebase/app';
import { getDatabase } from "firebase/database";
// TODO: Replace with your app's Firebase project configuration
const firebaseConfig = {
apiKey: "AIzaSyB2CFjr32PoNdsnfvEgt_AijgE18lNKz2c",
authDomain: "chat-app-42ed5.firebaseapp.com",
projectId: "chat-app-42ed5",
storageBucket: "chat-app-42ed5.appspot.com",
messagingSenderId: "880643875911",
appId: "1:880643875911:web:9d04114b45bb40c2627d62",
measurementId: "G-MP1VZCGRDP"
};
const app = initializeApp(firebaseConfig);
// Get a reference to the database service
const database = getDatabase(app);
export default database
When you mount the App component, you can open a snapshot listener to RTDB, see docs. You do that in useEffect and also return a function from useEffect to destroy the listener when your component gets unmounted. It's called a "cleanup function", see React docs
It would look something like that:
function App() {
useEffect(() => {
const unsubListener = onValue(ref(rtdb_instance, '/YOUR_PATH'), (snapshot) => {
// Probably iterate over `snapshot.val()` and store it in your state
})
// Return cleanup function
return unsubListener;
}, [])
}
Note that the dependency array is empty here, meaning it won't change. Hence, it will only be executed on inital mount.
Also please note that the code above is not tested, so your milage might vary.
I use my function writeUserData() in AddMessage function after submit message to send to REALTIME DATABASE not in UseEffect() and after to get message after refresh page i use onValue() as you say in UseEffect() and in the same time i update my state with setState() ex: if (data) {
setState({messages : data.messages , pseudo : state.pseudo})
} return
import React,{useState,useEffect} from 'react'
import Formulaire from './component/Formulaire'
import Message from './component/Message'
import {useParams} from 'react-router-dom'
import database from './Base'
import { ref, set,onValue } from "firebase/database";
function App() {
const [state , setState] = useState({
messages : {},
pseudo : useParams().pseudo,
})
useEffect(()=>{
const resultDb = ref(database);
onValue(resultDb, (snapshot) => {
const data = snapshot.val();
if (data) {
setState({messages : data.messages , pseudo : state.pseudo})
} return
})
return resultDb
},[])
function writeUserData(message) {
set(ref(database), {
messages :message
});
}
const addMessage = (message) =>{
state.messages[`message-${Date.now()}`] = message
setState({pseudo : state.pseudo ,messages : state.messages})
writeUserData(state.messages)
}
const msg = Object.keys(state.messages)
const lastMessages = msg.map(key=>{
return <Message key={key} pseudo={state.messages[key].pseudo} message = {state.messages[key].message} />
})
return (
<div className="container-lg col-4 mt-5">
<div>
{lastMessages}
</div>
<Formulaire length={150} addMessage ={addMessage} pseudo={state.pseudo} />
</div>
)
}
export default App

Why can I not use auth.currentuser.uid in getServerSideProps?

It may be a fundamental concept but I can't find anything to my issue.
const userDoc = getUserWithUID(auth.currentUser.uid);
Cannot read property 'uid' of null
import ImageUploader from "../components/ImageUploader";
import {auth, getUserWithUID, postToJSON} from "../lib/firebase";
import {useContext} from "react";
import {UserContext} from "../lib/context";
export async function getServerSideProps(context) {
const userDoc = getUserWithUID(auth.currentUser.uid);
return {props: {userDoc}} ;
}
export default function SettingsPage() {
return (
<main>
<ImageUploader></ImageUploader>
</main>
)
/ Firebase lib file
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import 'firebase/storage';
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_API_KEY,
authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_APP_ID,
measurementId: process.env.NEXT_PUBLIC_MEASUREMENT_ID
};
if(!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
export const auth = firebase.auth();
export const googleAuthProvider = new firebase.auth.GoogleAuthProvider();
export const firestore = firebase.firestore();
// Storage exports
export const storage = firebase.storage();
export const STATE_CHANGED = firebase.storage.TaskEvent.STATE_CHANGED;
export const fromMillis = firebase.firestore.Timestamp.fromMillis;
export const serverTimestamp = firebase.firestore.FieldValue.serverTimestamp;
/**`
* Gets a users/{uid} document with company
* #param {string} company
*/
export async function getUserWithCompany(company) {
const usersRef = firestore.collection('users');
const query = usersRef.where('company', '==', company).limit(1);
const userDoc = (await query.get()).docs[0];
return userDoc;
}
export async function getUserWithUID(uid) {
const userDoc = await firestore.collection('users').doc(uid).get();
return userDoc;
}
As far as my understanding goes, getServerSideProps() is only executed on the server and therefore has no access to frontEnd Data such as the Google authentication token and the corresponding userID. Hope this helps, even though you asked one month ago. :)

Reactjs Firebase: Uncaught TypeError: (0 , _config_firebase__WEBPACK_IMPORTED_MODULE_2__.default) is not a function

I'm pretty new to react and firebase. I implemented a login/auth system with firebase, and I was trying to take this to the next level: implement a profile page with profile picture. I followed a youtube tutorial and it seemed pretty easy,but somehow I'm getting a firebase error, and I assume something changed since the release of that tutorial! I don't really know what is going on, please help me fix this and explain me like I'm 5!
(update profile picture file)
ProfilePicture.js:
import React, {useEffect, useState} from 'react'
import {useAuth} from '../authentication/AuthContext'
import upload from '../../config/firebase'
export default function ProfilePicture() {
const currentUser = useAuth()
const [photo, setPhoto] = useState(null)
const [loading, setLoading] = useState(false)
const [photoURL, setPhotoURL] = useState("https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png")
function handleChange(e){
if (e.target.files[0]){
setPhoto(e.target.files[0])
}
}
function handleClick(){
upload(photo, currentUser, setLoading)
}
useEffect(()=>{
if (currentUser && currentUser.photoURL){
setPhotoURL(currentUser.photoURL)
}
}, [currentUser])
return (
<>
<input type="file" onChange={handleChange} />
<button disabled={loading || !photo} onClick={handleClick}>Upload</button>
<img src={photoURL} alt="Avatar" className='avatar'/>
</>
)
}
firebase.js
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import 'firebase/firestore'
import { getStorage, ref, uploadBytes } from 'firebase/storage';
const app = firebase.initializeApp({
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_FIREBASE_APP_ID
})
export const auth = firebase.auth();
export const firestore = firebase.firestore();
export const storage = getStorage();
//storage
export async function upload(file, currentUser, setLoading){
const fileRef = ref(storage, currentUser.uid + '.png')
setLoading(true)
const snapshot = await uploadBytes(fileRef, file)
setLoading(false)
alert("Uploaded File!")
}
export const createUserDocument = async (user, additionalData) => {
if (!user) return;
const userRef = firestore.doc(`users/${user.uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { email } = user;
const { displayName } = additionalData;
try {
await userRef.set({
displayName,
email,
createdAt: new Date(),
});
} catch (error) {
console.log('Error creating user', error);
}
}
};
export default app
the error:
You are mixing up firebase compat version with the latest firebase modular version
The following lines are not needed.
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import 'firebase/firestore'
https://firebase.google.com/docs/web/modular-upgrade
You may want to revisit the documentation, and only follow the codes under version 9 modular.
For example, to initialize the app, the correct codes for version 9 modular will be something like this.
import { initializeApp, getApps } from "firebase/app"
var firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECTID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGEBUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID
};
For authentication you need to use getAuth
For example
import firebase from 'src/firebase/firebase'
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
const auth = getAuth(firebase);
export async function loginPassword(email, password) {
return await signInWithEmailAndPassword(auth, email,password)
}
Reference: https://firebase.google.com/docs/auth/web/password-auth
Please follow the codes from v9 modular

Resources