I want to get 'sample' document in Firestore using getServerSideProps , I want to get my blog post with matching url. Code below doesn't work. It's result is 'can't read' What should I do? or is there the other way?
export async function getServerSideProps({params}) {
let {slagUrl} = params;
const article = [];
const articleRef = collection(db, "BlogArticles");
const q = query(articleRef, where("slug", "==", slagUrl));
onSnapshot(q, (snapshot) => {
const articles = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
console.log('query',articles[0])
article.push(articles[0]);
});
return {
props: { article }, // will be passed to the page component as props
}
}
Related
React code
import React, { useEffect, useState } from "react";
import { getDocs, collection } from "firebase/firestore";
import { auth, db } from "../firebase-config";
import { useNavigate } from "react-router-dom";
function Load() {
const navigate = useNavigate();
const [accountList, setAccountList] = useState([]);
const [hasEmail, setHasEmail] = useState(false);
const accountRef = collection(db, "accounts");
Am i using useEffect correctly?
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
setAccountList(
data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}))
);
};
getAccounts();
emailCheck();
direct();
}, []);
checking whether email exists
const emailCheck = () => {
if (accountList.filter((e) => e.email === auth.currentUser.email)) {
setHasEmail(true);
} else {
setHasEmail(false);
}
};
Redirecting based on current user
const direct = () => {
if (hasEmail) {
navigate("/index");
} else {
navigate("/enterdetails");
}
};
return <div></div>;
}
The code compiles but doesn't redirect properly to any of the pages.
What changes should I make?
First question posted excuse me if format is wrong.
There are two problems here:
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
setAccountList(
data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}))
);
};
getAccounts();
emailCheck();
direct();
}, []);
In order:
Since getAccounts is asynchronous, you need to use await when calling it.
But even then, setting state is an asynchronous operation too, so the account list won't be updated immediately after getAccounts completes - even when you use await when calling it.
If you don't use the accountList for rendering UI, you should probably get rid of it as a useState hook altogether, and just use regular JavaScript variables to pass the value around.
But even if you use it in the UI, you'll need to use different logic to check its results. For example, you could run the extra checks inside the getAccounts function and have them use the same results as a regular variable:
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
const result = data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
setAccountList(result);
emailCheck(result);
direct();
};
getAccounts();
}, []);
const emailCheck = (accounts) => {
setHasEmail(accounts.some((e) => e.email === auth.currentUser.email));
};
Alternatively, you can use a second effect that depends on the accountList state variable to perform the check and redirect:
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
setAccountList(
data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}))
);
};
getAccounts();
});
useEffect(() => {
emailCheck();
direct();
}, [accountList]);
Now the second effect will be triggered each time the accountList is updated in the state.
I am trying to recreate a chat app clone with firebase version 9 from firebase version 7.
I am trying to load the chat messages for the two users when the user navigates to the specific chat.
I have tried everything but can't seem to solve the problem. The following is the old piece of code from firebase version 7
export async function getServerSideProps(context) {
const ref = db.collection('chats').doc(context.query.id);
// Prep the messages array for the chat
const messagesRes = await ref
.collection('messages')
.orderBy('timestamp', 'asc')
.get();
const messages = messagesRes.docs.map((doc) => {
return {
...doc.data(),
id: doc.id,
}
}).map(messages => ({
...messages,
timestamp: messages.timestamp.toDate().getTime(),
}))
// Prep the users array for the chat
const chatRes = await ref.get();
const chat = {
...chatRes.data(),
id: chatRes.id,
}
return {
props: {
messages: JSON.stringify(messages),
chat,
}
}
}
function ChatConversation({
chat,
messages
}) {
const [user] = useAuthState(auth);
const endOfMessagesRef = useRef(null);
const scrollToBottom = () => {
endOfMessagesRef.current.scrollIntoView({
behaviour: 'smooth',
block: 'start',
});
};
return (
<>
<ChatScreen chat={chat} messages={messages} />
</>
)
}
Here is the rewritten code in firebase version 9:
export async function getServerSideProps(context) {
const ref = doc(db, "chats", context.query.id);
// Prep the messages array for the chat
const messagesRef = collection(db, 'chats', context.query.id, 'messages');
const messagesRes = await getDocs(query(messagesRef, orderBy("timestamp", "asc")))
const messages = messagesRes.docs.map((doc) => {
return {
...doc.data(),
id: doc.id,
}
}).map(messages => ({
...messages,
timestamp: messages.timestamp.toDate().getTime(),
}))
// Prep the users array for the chat
const chatRes = await getDocs(ref);
const chat = {
...chatRes.data(),
id: chatRes.id,
}
return {
props: {
messages: JSON.stringify(messages),
chat,
}
}
}
function ChatConversation({
chat,
messages
}) {
const [user] = useAuthState(auth);
return (
<>
<ChatScreen chat={chat} messages={messages} />
</>
)
}
What can I do to get rid of this error?
The messagesRef is a subcollection reference inside a specific document in a chat collection.
const ref = doc(db, "chats", context.query.id);
const messagesRef = collection(ref, 'messages');
This is the same syntax below:
const messagesRef = collection(doc(db, "chats", context.query.id), 'messages');
Then you can put your query inside a variable
const q = query(messagesRef, orderBy("timestamp", "asc");
const docSnap = await getDocs(q);
I made this function that updates with a callback function when the firestore database changes.
export function realTimeData(collectionName, callback, queryForced) {
const dbRef = collection(db, collectionName);
const queryOrdered = query(
collection(db, collectionName),
orderBy("time", "desc"),
limit(50)
);
const unsub = onSnapshot(queryForced || queryOrdered, (snapshot) => {
const result = [];
snapshot.docs.forEach((doc) => {
result.push({
id: doc.id,
...doc.data(),
});
});
unsubs.push(unsub);
console.log("realTimeData", result);
callback({ [collectionName]: result });
});
}
Now I was wondering if this is correct? Does it always update the data when I unmount the react component?
My react component below:
function App() {
const [history, setHistory] = useState({ ttd: [] });
useEffect(() => {
realTimeData("ttd", setHistory);
}, []);
// etc ..
my code
export const getServerSideProps: GetServerSideProps = async () => {
const ref = collection(db, "books");
const results = [];
const unsub = onSnapshot(ref, (snapshot) => {
snapshot.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
//here i get the results
console.log(results)
});
// here is empty
console.log(results)
return {
props: {
books: results,
},
};
};
I'm trying to get the real time data from firestore database on the getServerSideProps function, inside the snapshot I can get the results, but when it's outside the array it's empty and I can't pass to props.
Instead of using onSnapshot I would use getDocs (you need to import from 'firebase/firestore'):
export const getServerSideProps: GetServerSideProps = async () => {
const [results, setResults] = useState([]);
const ref = collection(db, 'books');
const snapshot = await getDoc(docRef);
articlesSnapshot.forEach((doc) => {
setResults((oldArray) => [...oldArray, doc.data()]);
});
return {
props: {
books: results,
},
};
};
I was also stucked in the same problem.
Here we should use get() instead of snapshot cause next will take care of your updated data under the hood.
so rewrite your code like this
export async function getServerSideProps(context) {
const resultref = await db.collection("books").get();
const results = resultRef.docs.map((doc)=>({
id: doc.id,
resultData: doc.data()
}))
return {
props: { books: JSON.parse(JSON.stringify(results)) },
}
}
so map function will return the new array in variable results
which is then deep copied and passed as props
I hope this will give you the desired result 😀
I want to fetch single data by id and I am using getStaticPaths and getStaticProps but I am getting the error data is not defined. Where am I going wrong Please help
My [id].tsx file
import MainComponentLayout from "../../components/Layout/MainLayoutComponent"
import EditProject from "../../components/EditProjectForm";
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`)
const data = await response.json()
console.log(data)
const path = data.result.map(project => {
return{
params: {id:project.project_ID}
}
})
return{
paths:path,
fallback: false
}
}
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects/${id}`)
// Single Object
const data = await response.json()
return{
props: {fetchedData:data},
}
}
const EditForm = () => {
return(
<MainComponentLayout ComponentToRender = {<EditProject fetchedData = {fetchedData}/>}/>
)
}
export default EditForm
Change const EditForm = () => { to const EditForm = ({fetchedData}) => and it will work.
The getStaticProps, as its name implies, passes the fetched props object to the function as properties. You need to define them in the function as an object, and you can also destructure as in the example above, basically defining a fetchedData variable.
If You want to use props {fetchedData:data} in your app, You need pass them to the page component as props. As we can read in docs:
props - An optional object with the props that will be received by the
page component. It should be a serializable object
Here You have example page with getStaticProps() correctly used.
and Your code with props, Good Luck ! ;-)
import MainComponentLayout from "../../components/Layout/MainLayoutComponent";
import EditProject from "../../components/EditProjectForm";
const EditForm = ({ fetchedData }) => {
return (
<MainComponentLayout
ComponentToRender={<EditProject fetchedData={fetchedData} />}
/>
);
};
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id;
const response = await fetch(
`http://b560-45-248-23-129.ngrok.io/projects/${id}`
);
// Single Object
const data = await response.json();
return {
props: { fetchedData: data },
};
};
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`);
const data = await response.json();
console.log(data);
const path = data.result.map((project) => {
return {
params: { id: project.project_ID },
};
});
return {
paths: path,
fallback: false,
};
};
export default EditForm;