onSnapshot firebase, getServerSideProps - reactjs

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 😀

Related

Change data on two different querys from React Query

so im doing a little application of a pokedex utilizing the pokeapi to test react query, what i am trying to do is first fetch the data on mount and after that, if i use the search button the data already fetched to change something like this
fetch on mount
and the search
search fetch
something easy to do with useState but i am having problems with react query
i got something like this
pokedex
at the mount of the component i have this
export const fetchPokemon = async (URL: string) => {
const result = await axios.get<pokemon>(URL);
return result;
};
export const fetchPokemons = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=20&offset=0s";
const { data } = await axios.get<pokemons>(URL);
const result = await Promise.all(
data.results.map(async (pokemon) => {
return fetchPokemon(pokemon.url);
})
);
return result;
};
export const useAllPokemons = () => {
return useQuery({
queryKey: ["pokemons"],
queryFn: fetchPokemons,
});
};
const { data, isLoading } = useAllPokemons();
works great but now i want to search pokemons with a search button like in the image and to replace the initial data that i already fetch so only the data that i searched appears so i did this
export const fetchAllPokemons = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=100";
const { data } = await axios.get<pokemons>(URL);
const result = await Promise.all(
data.results.map(async (pokemon) => {
return fetchPokemon(pokemon.url);
})
);
return result;
};
let { data, refetch } = useQuery({
queryKey: ["pokemons"],
queryFn: fetchAllPokemons,
enabled: false,
select: (data) => {
const pokemonData = data.map((pokemon) => {
if (pokemon.data.name.startsWith("char")) {
return pokemon;
}
});
return pokemonData;
},
});
<button
onClick={() => {
refetch();
}}
>
asd
</button>
and nothing happens, but when i open the console the data is changing but then again returns to the initial fetch
I guess you should use the onSuccess:
const {data} = useQuery("fetchData", fetchData, {
onSuccess: (data) => {
// do something with your data and return
}
});

Hi, i'm retrieving data from firestore, and checking whether to direct the user to index page or to enter details for a new user But not able to do so

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.

how to call firebase inside the .nextjs getServerSideProps

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
}
}

Realtime data from firestore in react, is this good practice?

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 ..

getStaticProps returns data not defined In nextjs

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;

Resources