I'm using react with firebase/firestore, I'm moving from Angular to React but I'm struggling to access the firestore database without AngularFirestore. I am able to login to firestore and obtain the user.uid, I just can't access the 'pred' collection which is nested inside each user.uid so this data is only for the user.
In Angular I access my database like this: (it works)
this.auth.user.pipe(take(1)).subscribe((user) => {
if (user) {
this.items = this.db
.collection("users")
.doc(this.user.uid)
.collection("pred")
.valueChanges();
In react I'm trying to do the same collection, doc, collection, but I can't find clear documentation how to do it.
My react attempt:
import React, { useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useNavigate } from "react-router-dom";
import "./Dashboard.css";
import { auth, db, logout } from "../../services/firebase/firebase-auth";
import { query, collection, getDocs, where, doc } from "firebase/firestore";
function Dashboard() {
const [user, loading, error] = useAuthState(auth);
const [name, setName] = useState("");
const navigate = useNavigate();
const fetchData = async () => {
const userId = auth.currentUser?.uid;
const docRef = collection(db, "users");
const predictions = collection(db, "pred");
const predSnapshot = await getDocs(predictions);
const predList = predSnapshot.docs.map((doc) => doc.data());
return predList;
};
How can I return the predList which follows:
.collection("users")
.doc(this.user.uid)
.collection("pred")
path order, but using react or just plain javascript? I'm using this import { query, collection, getDocs, where, doc } from "firebase/firestore"; package but I can't see how to do it.
To create a reference to the pred subcollection of the current user in the new modular/v9 syntax:
collection(db, "users", userId!, "pred")
const fetchData = async () => {
const userId = auth.currentUser?.uid;
const docRef = collection(db, "users", userId as string, "pred");
const preds = await getDocs(docRef);
const predList = preds.docs.map((doc) => doc.data());
console.log(predList);
};
Related
I'm using react-firebase-hook , and I'm trying to check if the user is admin or not, and I want to it to be a global state where I don't have to add this code in every and each component to check if the user is admin or not, here is the code..
import { useState, useEffect } from 'react';
import { query, collection, getDocs, where } from "firebase/firestore";
import { auth, db } from "../../config/fbConfig";
import { useAuthState } from "react-firebase-hooks/auth";
const CreateAnn = () => {
const [ann, setAnn] = useState(''); // ignore this
const [admin, setAdmin] = useState(false);
const [user] = useAuthState(auth);
const fetchAdmin = async () => {
try {
const q = query(collection(db, "users"), where("uid", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
if(data.admin === true) {
setAdmin(true);
}
else { setAdmin(false); }
} catch (err) {
// do nothing
}
};
useEffect(() => {
fetchAdmin();
});
I want to have this as a global state, tried to useContext but i think I'm using it the wrong way, so anyone can help?
You are correct to use a context, however, you might use it wrong as you said.
You should set up a context that handles the currently logged in user.
In this context you can also fetch the extra details of the user from the user collection.
Also, you can grab the user directly with ID instead of where:
const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);
const data = docSnap.exists ? docSnap.data() : undefined
Follow this link to set up the context of auth correct.
https://dev.to/dchowitz/react-firebase-a-simple-context-based-authentication-provider-1ool
I would like to know how to store the data in the cache when the user come to my website first time and for the subsequent visit I want to fetch the data from cache and only want to fetch new data/updated data from the server.
Right now, it fetches data every time user comes to my website, which causes a lot of reads, so I want to reduce those reads by storing data in the cache.
My code:
import { useEffect, useState } from "react"
// firebase import
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 = []
snapshot.docs.forEach(
(doc) => {
results.push({ ...doc.data(), id: doc.id })
},
(error) => {
console.log(error)
setError("could not fetch the data")
}
)
// update state
setDocuments(results)
setIsLoading(false)
setError(null)
})
// unsubscribe to the previous listener before running the side effect again
return () => unsubscribe()
}, [openTab])
return { documents, error, isLoading }
}
I am trying to implment a Chat functionality in my React Native App.
The Problem i am facing is, that if i send a message it doesnt get saved in the Firestore Database. But if i remove the useLayoutEffect Function, the Messages get Saved in Firestore.
Ive got the code from this Page:
Here is my Code:
import React, {useCallback, useLayoutEffect, useState} from 'react';
import {addDoc, collection, query, orderBy, onSnapshot} from 'firebase/firestore';
import {db} from '../../../firebase';
import {GiftedChat} from 'react-native-gifted-chat';
export const EventChatScreen = () => {
const [messages, setMessages] = useState([])
useLayoutEffect(() => {
const collectionRef = collection(db, 'chats');
const q = query(collectionRef, orderBy('createdAt', 'desc'));
return onSnapshot(q, QuerySnapshot =>
{
setMessages(
QuerySnapshot.docs.map(doc => ({ // these key:value pairs are in a required format for GiftedChat
_id: doc.data()._id,
createdAt: doc.data().createdAt.toDate(),
text: doc.data().text,
user: doc.data().user
}))
);
});
}, []);
const onSend = useCallback((messages = []) => {
setMessages(previousMessages =>
GiftedChat.append(previousMessages, messages)
);
const { _id, createdAt, text, user } = messages[0];
addDoc(collection(db, 'chats'), {
_id,
createdAt,
text,
user
});
}, []);
return (
<GiftedChat
messages={messages}
showAvatarForEveryMessage={true}
onSend={messages => onSend(messages)}
user={{
_id: 1,
avatar: 'https://i.pravatar.cc/300'
}}
/>
);
}
export default EventChatScreen
As i mentioned before. As soon as i remove the useLayoutEffect it saves the Data to Firebase.
Ive also tried to get the Return Value of the Add Doc. But as soon as the useLayoutEffect function is added, it doesnt return a Value.
EDIT: Now it works while i am in the Debug Mode.
As the title says, I'm trying to build out a blog site and I have most of it done expect for clicking on a post stub to read the full post. I keep getting this error anytime I go to the post page.
Error: Error serializing .post returned from getServerSideProps in "/posts/[slug]".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
I've searched everywhere to try and figure out what I'm doing wrong but can't seem to find the right answer.
Here is by firebase code.
import {
collection,
getDocs,
getFirestore,
limit,
onSnapshot,
orderBy,
query,
doc,
setDoc,
getDoc,
} from "firebase/firestore";
import firebase from "firebase/app";
import { initializeApp, getApps, getApp } from "firebase/app";
import { getAuth, GoogleAuthProvider } from "firebase/auth";
import { Timestamp, toJSON } from "firebase/firestore";
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyAB4SbXl-I1TMoa31ybnCzmTASXjZFnMAg",
authDomain: "personal-blog-8decb.firebaseapp.com",
projectId: "personal-blog-8decb",
storageBucket: "personal-blog-8decb.appspot.com",
messagingSenderId: "473768411808",
appId: "1:473768411808:web:c464d23c531b8bdaa4bfc5",
measurementId: "G-6F04591W4N",
};
if (!getApps().length) {
initializeApp(firebaseConfig);
}
const db = getFirestore();
//Reads all the posts in the database
export const getPosts = async () => {
const q = query(collection(db, "posts"), orderBy("date", "desc"));
const querySnapShot = await getDocs(q);
const posts = querySnapShot.docs.map((doc) => ({
...doc.data(),
id: doc.id,
date: doc.data().date?.toDate().getTime(),
}));
return posts;
};
// Get one post from database based on the slug.
export const getPostBySlug = async (slug) => {
const docRef = doc(db, "posts", `${slug}`);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
return docSnap.data();
} else {
console.log("No Such Document");
}
};
// Adds posts to the database
export const createPost = async (post) => {
await setDoc(doc(db, "posts", `${post.slug}`), {
title: post.title,
content: post.content,
date: Timestamp.fromDate(new Date()),
});
};
export const auth = getAuth();
export const googleAuthProvider = new GoogleAuthProvider();
And here is the slug page.
import { async } from "#firebase/util";
import { useRouter } from "next/router";
import { getPostBySlug } from "../../lib/firebase";
import moment from "moment";
export async function getServerSideProps() {
const post = await getPostBySlug();
return {
props: {
post,
},
};
}
export default function PostPage({ post }) {
<div className="post">
<h1>{post.title}</h1>
<span>Published {moment(post.date).format("LL")}</span>
<p dangerouslySetInnerHTML={{ __html: post.content }}></p>
</div>;
}
Thanks in advance.
So here is how I got it to work. First I changed the firebase read file to a custom query.
// Get one post from database based on the slug.
export const getPostBySlug = async (slug) => {
const q = query(collection(db, "posts"), where("slug", "==", `${slug}`));
const querySnapShot = await getDocs(q);
const post = querySnapShot.docs.map((doc) => ({
...doc.data(),
id: doc.id,
date: doc.data().date?.toDate().getTime(),
}));
return post;
I had to do it this way because of the timestamp I used with firestore. I couldn't figure out any other way to serialize it to json.
Next I changed the getServerSideProps function in the slug js file to take a context query.
export async function getServerSideProps(context) {
const post = await getPostBySlug(context.query.slug);
console.log("this is the severside props: ", post);
return {
props: {
post,
},
};
}
After that it worked fine. There are probably better approached to this but this is what worked for me.
I'm a newly self-taught coder. Hopefully I'm expressing my situation adequately.
I'm trying to retrieve some data from Cloud Firestore. I've made a custom hook that should useState and setDocuments to the be the retrieved "guide". The query uses useParams to get the id to match to the specific guide I'm trying to get. I think the issue is in the querySnapshot. setDocuments doesn't seem to be working. When I console log "documents" it's an empty array.
Any leads?
import { useParams } from 'react-router-dom'
import { collection, query, where, getDocs } from "firebase/firestore";
import { db } from "../firebase/config"
import { useEffect } from 'react';
export const useGuide = (c) => {
const [documents, setDocuments] = useState([])
const { id } = useParams()
useEffect(() => {
const ref = collection(db, c)
const q = query(ref, where("id", "==", `${id}`))
getDocs(q).then(querySnapshot => {
querySnapshot.forEach((doc) => {
setDocuments(doc.data())
});
});
}, [])
console.log(documents)
return { documents }
}
Here is where I try to use the hook useGuide to set the state which would be passed to a component.
import SingleGuide from '../../components/SingleGuide/SingleGuide'
import { useGuide } from '../../hooks/useGuide'
function Guide() {
const { documents: guides } = useGuide('guides')
console.log(guides)
return (
<div>
{guides && <SingleGuide guide={guides}/>}
</div>
)
}
export default Guide
There are a few issues with your code including setting an array of docs equal to a single doc in the query results. Try something like the following.
import { useParams } from "react-router-dom";
import { collection, query, where, getDocs } from "firebase/firestore";
import { db } from "../firebase/config";
import { useState, useEffect } from "react";
export const useGuide = (c) => {
const [documents, setDocuments] = useState([]);
const { id } = useParams();
useEffect(() => {
// create array to hold all the individual docs
const docs = [];
const ref = collection(db, c);
const q = query(ref, where("id", "==", `${id}`));
getDocs(q).then((querySnapshot) => {
querySnapshot.forEach((doc) => {
docs.push(doc.data());
});
setDocuments(docs);
});
}, [c]); // could consider passing `id` to the hook instead of useParams and adding that as a dependency to update data automatically
console.log(documents);
return { documents };
};