I am working with React Query for the first time in this project and I like it so far. I am still fairly new to it and am running into an issue that I cannot seem to solve. When I call the useQuery hook, it calls the data fine and it even prints out the data after it is done loading. The only problem is that I am printing the data inside the react function outside the render and so it prints whenever. My issue is how to wait for the data to finish loading so that I can format the information and render it. There is more that I need to do so that the information is useful, and so that requires adding more information, such as an API call. Should I put this information inside of the useQuery hook, useEffect, or is there another way to wait for the data to start loading so that I can work with it?
useQuery hook
const {data, status} = useQuery(['firestoreData'],
async () => {
const q = query(collection(db, loc));
const snapshot = await getDocs(q)
let arr = []
snapshot.forEach(doc => {
arr.push(doc.data())
})
// arr.forEach(doc => {
// const tickerSymbol = doc.stockTicker;
// if (averagePriceMap.has(tickerSymbol)) {
// console.log("Has")
// let data = averagePriceMap.get(tickerSymbol)
// const type = doc.type;
// let newShares = parseFloat(data.shares);
// let newPrice = parseFloat(data.price);
// const oldAmount = newShares * newPrice
// if (type === "buy") {
// newShares = newShares + parseFloat(doc.shares);
// newPrice = (oldAmount + parseFloat(doc.price))/newShares;
// } else {
// newShares = newShares - parseFloat(doc.shares);
// newPrice = (oldAmount - parseFloat(doc.price))/newShares;
// }
// const newData = {
// price: newPrice,
// shares: newShares,
// stockTicker: tickerSymbol,
// id: doc.id
// }
// averagePriceMap.set(tickerSymbol, newData)
// } else {
// console.log("Doesnt Have")
// avgMap.set(tickerSymbol, doc)
// // const
// // setAverageMap(new Map(averageMap.set(String.valueOf(tickerSymbol), doc)))
// }
// })
return Promise.all(arr)
}
)
console.log(data)
The commented-out information is me trying to implement what Im doing inside of my useEffect in order to format the data.
UseEffect hook
useEffect(() => {
// console.log(data)
if (!currentUser) {
router.push('/login');
} else {
if(dataFetchedRef.current) return;
dataFetchedRef.current = true;
const setData = async() => {
// let data = documents
// while (status === 'loading') {
// setTimeout(() => {return}, 100)
// }
let avgMap = new Map()
// const loc = getLoc();
// const q = query(collection(db, loc));
// const snapshot = await getDocs(q)
data?.forEach(doc => {
// snapshot.forEach(async doc => {
// const obj = doc.data()
const documentObj = {
price: doc.data().price,
shares: doc.data().shares,
ticker: doc.data().stockTicker,
type: doc.data().type,
documentID: doc.id,
}
console.log(documentObj)
data.push(documentObj)
let tic = doc.stockTicker;
console.log(tic)
if (avgMap.has(tic)) {
console.log("Has")
let data = avgMap.get(tic)
const type = data.type;
let newShares = parseFloat(data.shares);
let newPrice = parseFloat(data.price);
const oldAmount = newShares * newPrice
if (type === "buy") {
newShares = newShares + parseFloat(doc.shares);
newPrice = (oldAmount + parseFloat(doc.price))/newShares;
} else {
newShares = newShares - parseFloat(doc.shares);
newPrice = (oldAmount - parseFloat(doc.price))/newShares;
}
const newData = {
price: newPrice,
shares: newShares,
stockTicker: tic,
id: doc.documentID
}
avgMap.set(tic, newData)
setAverageMap(new Map(averageMap.set(String.valueOf(tic), newData)))
} else {
console.log("Doesnt Have")
avgMap.set(tic, doc.data())
setAverageMap(new Map(averageMap.set(String.valueOf(tic), doc.data())))
}
})
console.log(avgMap)
const retList = listDocuments
const refPrice = averagePrice
const refEquitiesList = equities
avgMap.forEach(async (value, key) => {
const newPrice = await getStockPrice(key).then(result => result)
const currentPrice = parseFloat(newPrice.toFixed(2))
const pl = (parseFloat(value.shares)*(currentPrice - parseFloat(value.price))).toFixed(2)
const fixedPrice = (value.price).toFixed(2)
const totalEq = (parseFloat(value.shares) * currentPrice).toFixed(2)
let insertAvg = {
ticker: key,
shares: value.shares,
averagePrice: fixedPrice,
currentPrice,
profitLoss: pl,
dividendYield: "Coming Soon"
}
setTicker([...tickers, key]);
setReturns([...returns, pl])
retList.push(insertAvg)
refPrice.key = insertAvg
refEquitiesList.push({
name: key,
value: totalEq
})
// setListDocuments([...listDocuments, insertAvg])
// console.log(retList)
// console.log(listDocuments)
})
let arr = (listDocuments)
console.log(tickers);
console.log(returns)
console.log(arr)
console.log(averagePrice)
listDocuments.map(function(doc) {
console.log(doc)
})
console.log(equities)
// setAverageMap(avgMap)
}
console.log(averageMap)
setData()
console.log(documents)
console.log(listDocuments)
setLoading(false)
}
}, [documents, listDocuments])
When the useEffect first runs, it is returning undefined because data still has not loaded. That is fine, but how do I wait for data to load before running my formatting on it?
I recommend you writing the fetching function for useQuery in a separate place for improved readability.
For instance,
const fetchDocs = (query) => {
return getDocs(query);
}
If you want to refetch when db changes, then add the one inside the query key array. I personally don't recommend processing response data from react-query with useEffect because you can do it inside onSuccess function in react-query.
const {data, isLoading, status} = useQuery(['firestoreData', db], fetchDocs(query(collection(db, loc))), {
onSuccess: (data) => {
// do something here, not in useEffect
}
});
...
if (isLoading) return <div>loading...</div>;
return <div>{/* your code */}</div>
// isFetching can be used for loading bar when refetching data
https://tanstack.com/query/v4/docs/react/guides/queries
Also the useEffect's dependency array, [documents, listDocument] probably makes your code rerun when it is unnecessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With