3

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?

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I am trying to print out data but it is not working, it returns undefined but even though it prints out the individual documents. how do i fix that. the fetcher function works as it console logs, but i don't know why it returns undefined @brandonWie
const fetchDocs = async (queryCollection) => { const snapshot = await getDocs(queryCollection); let arr = [] snapshot.forEach(doc => { console.log(doc.data()) arr.push(doc.data()) }) return Promise.all(arr) } const {data, status} = useQuery({queryKey: ['firestoreData', db]}, {queryFn: fetchDocs(query(collection(db, loc)))}, { onSuccess: (data) => { // do something here, not in useEffect console.log(data) } });
@Dotman Idk what exactly you mean from "it returns undefined but even though it prints out the individual documents". However, the data variable from useQuery function can be undefined if nothing is fetched. You should probably look for the API call if it works properly.
I fixed it with a quick modification, thank you so much

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.