Firestore uid not available after page reload - javascript

I am exporting a function returning firestore data useing the uid as identifier. The uid however is not available after reloading the page, causing af can't read null value error. I tried researching and happened upon the shown onAuthStateChanged, but this is causing the error: TypeError: Cannot read properties of undefined (reading 'indexOf'). Would appreciate the help.
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import {
getAuth,
onAuthStateChanged } from "firebase/auth";
import { useState, useEffect} from "react";
import {
getFirestore,
collection,
getDocs,
addDoc,
setDoc,
doc } from 'firebase/firestore';
const firebaseConfig = {
apiKey: "AIzaSyCJtckBTE3-ub4JP6NcEJX_PKao7r0YJRw",
authDomain: "dtustudenthub.firebaseapp.com",
projectId: "dtustudenthub",
storageBucket: "dtustudenthub.appspot.com",
messagingSenderId: "400034264848",
appId: "1:400034264848:web:f065a4bb76463063dd5795",
measurementId: "G-M5K2EJKLEL"
};
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
export const auth = getAuth(app);
const db = getFirestore();
export const GetAppointmentsFromFirebase = () => {
const [user, setUser] = useState({});
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
const appointmentColRef = collection(db, 'users', user?.uid, 'appointments');
let [schedulerData, setSchedulerData] = useState([])
useEffect(() => {
getDocs(appointmentColRef)
.then((snapshot) => {
let appointmentData = []
snapshot.docs.forEach((doc) => {
appointmentData.push({ ...doc.data() })
})
setSchedulerData(appointmentData);
console.log(appointmentData)
})
.catch(err => {
console.log(err.message)
})
}, []);
return schedulerData;
};

You should ideally run those queries only when the user state is loaded and initialize onAuthStateChanged() in useEffect(). Try refactoring the code as shown below:
export const GetAppointmentsFromFirebase = () => {
const [user, setUser] = useState({});
let [schedulerData, setSchedulerData] = useState([])
useEffect(() => {
onAuthStateChanged(auth, async (currentUser) => {
// Check if currentUser is null
if (currentUser) {
setUser(currentUser);
// Read user ID directly from user object
const appointmentColRef = collection(db, 'users', currentUser.uid, 'appointments');
const snapshot = await getDocs(appointmentColRef)
const data = snapshot.docs.map((d) => ({
id: d.id,
...d.data()
}))
setSchedulerData(data);
console.log(data);
} else {
console.log("No user logged in")
}
});
}, []);
return schedulerData;
};

Related

Unable to process request due to missing initial state( Firebase)

A user of my website encountered an error message while attempting to sign up using GitHub on my React site from his chrome browser(mobile). I have integrated Firebase GitHub sign-in with the popup method.
Unable to process request due to missing initial state. This may happen if browser sessionStorage is inaccessible or accidentally cleared.
Code for signup:
import { useEffect, useState } from "react"
import { GithubAuthProvider, signInWithPopup } from "firebase/auth"
import { auth } from "../firebase/config"
import { createUserProfileDocument } from "../firebase/createUserProfileDocument"
import { useAuthContext } from "./useAuthContext"
export const useSignup = () => {
const [error, setError] = useState(false)
const [isPending, setIsPending] = useState(false)
const [isCancelled, setIsCancelled] = useState(false)
const provider = new GithubAuthProvider()
const { dispatch } = useAuthContext()
const signup = async () => {
setError(null)
setIsPending(true)
try {
const res = await signInWithPopup(auth, provider)
if (!res) {
throw new Error("Could not complete signup")
}
const user = res.user
await createUserProfileDocument(user)
dispatch({ type: "LOGIN", payload: user })
if (!isCancelled) {
setIsPending(false)
setError(null)
}
} catch (error) {
if (!isCancelled) {
setError(error.message)
setIsPending(false)
}
}
}
useEffect(() => {
return () => setIsCancelled(true)
}, [])
return { signup, error, isPending }
}
useAuthContext code:
import { useContext } from "react"
import { AuthContext } from "../context/AuthContext"
export const useAuthContext = () => {
const context = useContext(AuthContext)
if (!context) {
throw Error("useAuthContext must be used inside an AuthContextProvider")
}
return context
}
AuthContext code
import { createContext, useEffect, useReducer } from "react"
import { onAuthStateChanged } from "firebase/auth"
import { auth } from "../firebase/config"
import { authReducer } from "../reducers/authReducer"
export const AuthContext = createContext()
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
user: null,
authIsReady: false,
})
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
dispatch({ type: "AUTH_IS_READY", payload: user })
})
return unsubscribe
}, [])
return (
<AuthContext.Provider value={{ ...state, dispatch }}>{children}</AuthContext.Provider>
)
}
firebaseConfig object:
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,
}
initializeApp(firebaseConfig)
const db = getFirestore()
const auth = getAuth()
export { auth, db, logEvent }

How to check whether I'm getting data from cache or firestore (JavaScript SDK)?

I enabled the firestore cache by calling the enableIndexedDbPersistence method in my react app. here is my code:
import { initializeApp } from "firebase/app"
import { getAuth } from "firebase/auth"
import { enableIndexedDbPersistence, getFirestore } from "firebase/firestore"
const firebaseConfig = {
// config code
}
initializeApp(firebaseConfig)
const db = getFirestore()
enableIndexedDbPersistence(db) // enable cache
const auth = getAuth()
export { auth, db }
I would like to know how I can verify that I'm getting data from the cache, not from the server in the below code?
import { useEffect, useState } from "react"
import { collection, limit, onSnapshot, orderBy, query, where } from "firebase/firestore"
import { db } from "../firebase/config"
export const useCollection = (c) => {
const [documents, setDocuments] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let ref = collection(db, c)
const unsubscribe = onSnapshot(ref, (snapshot) => {
const results = []
console.log(snapshot.metadata.fromCache ? "local data" : "server data")
snapshot.docs.forEach(
(doc) => {
results.push({ ...doc.data(), id: doc.id })
},
(error) => {
console.log(error)
setError("could not fetch the data")
}
)
setDocuments(results)
setIsLoading(false)
setError(null)
})
return () => unsubscribe()
}, [])
return { documents, error, isLoading }
}
Is there anything apart from calling the enableIndexedDbPersistence method I need to do to configure the cache properly?
Will this method cache the subcollection documents as well?

Why firebase give this error when I use onSnapshot?

Error itself : Uncaught FirebaseError: Expected type 'pa', but it was: a custom $n object
firebase file :
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore/lite'
const firebaseConfig = {
apiKey: 'API_KEY',
authDomain: 'AUTH_DOMAIN',
projectId: 'PROJECT_ID',
storageBucket: 'STORAGE_BUCKET',
messagingSenderId: 'MESSAGING_SENDER_ID',
appId: 'APP_ID',
}
const firebaseApp = initializeApp(firebaseConfig)
const db = getFirestore(firebaseApp)
const auth = getAuth(firebaseApp)
export { db, auth }
Request itself :
useEffect(() => {
//getPosts()
const unsubscribe = onSnapshot(collection(db, 'cities'), (snapshot) => {
const postsList = snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
setPosts(postsList)
})
return () => {
unsubscribe()
}
}, [])
I tried to change some imports like other recommend but just got another error
Firestore Lite SDK does not support listeners. Try importing getFirestore() from the standard SDK.
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore' // <-- remove /lite
const firebaseConfig = {...}
const firebaseApp = initializeApp(firebaseConfig)
const db = getFirestore(firebaseApp)
const auth = getAuth(firebaseApp)
export { db, auth }
import { db } from './path/to/firebase';
import { collection, onSnapshot } from 'firebase/firestore';
useEffect(() => {
const unsubscribe = onSnapshot(collection(db, 'cities'), (snapshot) => {
const postsList = snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
setPosts(postsList)
})
}, [])

NextJs getServerSideProps cannot fetch data from Cloud Firestore Web version 9

I am using NextJs version 12.0.10 and firebase version 9.6.6 which use a modular system to import it.
When I run the function from my service to fetch data from firebase/firestore, it returns an error saying Cannot access 'getStories' before initialization. I'm confident all the logic & syntax are correct, as it works perfectly when I fetch it from inside the page render function.
Here is my getServerSideProps function:
pages/index.js
import '#uiw/react-markdown-preview/markdown.css';
import { useContext } from 'react';
import { getStories } from '../lib/services/StoryService';
import { truncate } from 'lodash';
import { convertSecondsToHumanReadableDiff } from '../lib/utils/common';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { AuthContext } from '../pages/_app';
import Navigation from '../components/Navigation';
export async function getServerSideProps() {
const fetchedStories = await getStories();
const stories = fetchedStories.docs.map((story) => {
return {
...story.data(),
id: story.id,
content: truncate(story.data().content, { length: 150, separator: '...' }),
};
});
return { props: { stories } };
}
const Blog = ({ stories }) => {
const router = useRouter();
const { user } = useContext(AuthContext);
return (
<div>
...
</div>
);
};
export default Blog;
lib/firebase/firebase.js
import { initializeApp } from 'firebase/app';
import { getAnalytics } from 'firebase/analytics';
import { getFirestore } from 'firebase/firestore';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey: 'XXX',
authDomain: 'XXX',
projectId: 'XXX',
storageBucket: 'X',
messagingSenderId: 'XXX',
appId: 'XXX',
measurementId: 'XXX',
};
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
export const database = getFirestore(app);
export const auth = getAuth(app);
lib/services/storyService.js
import {
collection,
query,
getDocs,
getDoc,
setDoc,
doc,
serverTimestamp,
orderBy,
} from 'firebase/firestore';
import { database } from '../firebase/firebase';
import slugify from 'slugify';
import { random } from 'lodash';
const storiesRef = collection(database, 'stories');
export const createStory = async (payload) => {
const slugTitle = slugify(payload.title);
const slug = slugTitle + '-' + random(0, 100000);
const updatedPayload = {
...payload,
slug,
type: 'published',
createdAt: serverTimestamp(),
};
return setDoc(doc(storiesRef, slug), updatedPayload);
};
export const getStories = async () => {
const q = query(storiesRef, orderBy('createdAt', 'desc'));
return getDocs(q);
};
export const getStoryBySlug = async (slug) => {
const docRef = doc(database, 'stories', slug);
return getDoc(docRef);
};
You are using getDocs, a client-side function of firebase, inside your getStories function, which is invoked in getServerSideProps, a node.js environment.
Instead you need to use the admin SDK for functions invoked in node.js environment (like getServerSideProps), e.g. like so:
import * as admin from "firebase-admin/firestore";
export const getStories = async () => {
return await admin
.getFirestore()
.collection(database, 'stories')
.orderBy('createdAt', 'desc')
.get()
};
export const getStoryBySlug = async (slug) => {
return await admin
.getFirestore()
.doc(database, 'stories', slug)
.get()
};
(sorry for the late answer, I still hope it helps OP or anyone else)

GoogleAuthProvider is not a constructor

I'm still learning and I've been following tutorials on firebase auth with reactjs. Now I'm branching off into functionalities the tutorial doesn't cover (anonymous sign-in & linking to google) and I think I'm not understanding correctly how to use firebase's linkWithPopup.
I'm getting TypeError: firebase__WEBPACK_IMPORTED_MODULE_1_.default.auth.GoogleAuthProvider is not a constructor when I try do it. Here is the code:
firebase.js
import firebase from 'firebase/app';
import 'firebase/auth'
import 'firebase/firestore'
const app = firebase.initializeApp({
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
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 = app.auth();
export default app;
AuthContext.js:
import React, { useContext, useState, useEffect } from 'react'
import firebase, { auth } from '../firebase'
const AuthContext = React.createContext();
export const useAuth = () => {
return useContext(AuthContext);
}
const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const anonLogin = () => {
return auth.signInAnonymously();
}
const linkWithGoogle = () => {
var googleProvider = new firebase.auth.GoogleAuthProvider();
auth.currentUser.linkWithPopup(googleProvider)
.then(() => {
console.log('linked correctly');
})
.catch(error => {
console.error(error);
})
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
signup,
login,
logout,
resetPassword,
updateEmail,
updatePassword,
getUuid,
updateName,
anonLogin,
linkWithGoogle
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
}
Will appreciate any help I can get!
It looks like the Firebase reference is potentially a reference to your Firebase App rather than the Firebase library which contains the constructors for the providers.
try importing the Firebase library directly, or creating the providers inside your firebase.js file for you to export.
firebase.js
export const googleProvider = new firebase.auth.GoogleAuthProvider();
AuthContext.js
import firebase, { auth, googleProvider } from '../firebase'

Categories