Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for useQuery data to finish loading to start working with it NextJS

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?

like image 588
Dotman Avatar asked Nov 26 '25 03:11

Dotman


1 Answers

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.

like image 62
brandonwie Avatar answered Nov 28 '25 04:11

brandonwie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!