Why does my DBSearch function keep looping when pulling data from Firebase? - javascript

New to coding last month and I'm learning JS through React Native.
I've added a Firestore DB to pull data through to my app and put together a call-back function using the useState hook due to it being Async.
My issue is my DBSearch function is now looping infinitely.
Code below:
const [propertyData, setPropertyData] = React.useState(["Well, this is awkward..."])
const colRef = (listName) => collection(db, listName)
const dbSearch = (listName, callBackFuncHere) => {onSnapshot(colRef(listName), (snapshot) => {
let newList = []
snapshot.docs.forEach(doc => {
newList.push({ ...doc.data(), id: doc.id }) // Pulls Object Data and runs for loop to assign to newList Array
})
callBackFuncHere(newList)
})};
function retFunc(newList){
setPropertyData(newList)
console.log('1')
}
dbSearch('propertyList', retFunc)
Is this an improper use for useState? and how should I change my dbSearch function to stop it continually looping?
Apologies if this has been asked a thousand times before, I'm unsure how to clearly articulate and search for my problem.

Related

How to identify an element in a table whatever its place

Here is the new script with the find function which allows me to identify a single element of the array for sure but there is still a small problem. As you can see it's my const crypto which contains the data I want to display on the front end. However when I want to call crypto at the return level the const is not recognized.
Hello again,
I updated the script now it works I can display on the front end the data I want however I have the impression that the request to the api is executed several times when I would like there to be only one request
I put below a screen of the console.log of my script.
As you can see the data requested is displayed first as undefined then it is displayed several times, then I put blocked due to the too large number of requests made in little time
Thanks for your help
How do I make my requests :
import { useState, useEffect } from "react";
function App() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null)
useEffect(() => {
fetch("http://localhost:4500/coingeckotest")
.then(response => {
if (response.ok) {
return response.json()
}
throw response;
})
.then(data => {
setData(data)
})
.catch(error => {
console.error("Error fetching data: ", error)
setError(error)
})
.finally(() => {
setLoading(false)
})
}, [])
const uniswap = data&&data[0].tickers?.find(donne =>
donne.trade_url === 'https://app.uniswap.org/#/swap?
inputCurrency=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599&
outputCurrency=ETH')
const uniprice = uniswap?.converted_last?.usd
const sushiswap = data&&data[1].tickers?.find(donne =>
donne.trade_url === 'https://app.sushi.com/swap?
inputCurrency=0x2260fac5e5542a773aa44fbcfedf7c193bc2c59
9&outputCurrency=ETH')
const sushiprice = sushiswap?.converted_last?.usd
const curvefinance = data&&data[2].tickers?.find(donne =>
donne.base === 'DAI')
const curveprice = curvefinance?.converted_last?.usd
const quickswap = data&&data[3].tickers?.find(donne =>
donne.trade_url === 'https://quickswap.exchange/#/swap?
inputCurrency=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf127
0&outputCurrency=0xbbba073c31bf03b8acf7c28ef0738decf369
5683')
const quickprice = quickswap?.converted_last?.usd
console.log(uniprice)
console.log(sushiprice)
console.log(curveprice)
console.log(quickprice)
if (loading) return "Loading..."
if(error) return "Error !"
return (
<>
</>
)
}
export default App;
Thank you in advance for your answers
You can use the Array.find method to find out the first entry that matches a particular coin_id. The code for that would be:
const desiredCoinID = "something"; (determined by some user input)
const desiredCoinObject = data.find(coin => coin.coin_id === desiredCoinID);
const priceInUSD = desiredCoinObject?.tickers?.converted_last?.usd;
——-Updated Answer——-
Hi, this is my answer to your updated question. const crypto that you use is available only within the scope of the callback of the useEffect function, which is why you cannot see the results on your screen. There are two ways you can go about it:
First, you can declare crypto as a let outside the useEffect and then update it inside your useEffect. That way your crypto will have global scope. But there is a better way to do this, which is to use the useState hook.
You can declare a crypto, setCrypto using useState and then use the setter to update the value if crypto inside useEffect after the data fetching is over. Let me know if you need help writing code.

Trouble accessing firebase query results in document template

new to vue and firebase but stuck on this for hours. I have a vue3 app running a specific firebase9 query that will only return a maximum of 1 row (i.e. includes limit 1). The results are being returned but I am having trouble accessing the data so I can pass up to the template. I am using an example from a tutorial that was designed to return multiple rows from FB then iterate through in the template using v-for but in this case it will only ever be one row. All the tutorials I can find are similar not addressing how to deal with one row (or document) being returned. Oddly, the data is being returned to _rawValue but I can't seem to get to it...
Here's the JS snippet:
let collectionRef = collection(db, c)
collectionRef = query(collectionRef, where(...q), orderBy(...ob), limit(...lmt))
const unsub = onSnapshot(collectionRef, snapshot => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ ...doc.data(), id: doc.id })
})
// update values
documents.value = results
})
return { documents }
Here's the Vue snippet:
const { documents: lastEvent } = getCollectionRt(
'events',
['userId', '==', user.value.uid],
['created', 'desc'],
['1']
)
console.log('lastevent: ', lastEvent)
I can see that lastEvent does indeed contain an array with the values I am looking for so the query is running and returning, BUT, it is listed in something called "_rawValue" that I can't seem to access. For example I would like to set a variable to one of the values being returned like let myVar = lastEvent.id or lastEvent.created, etc.
[edit: use case is that I want to query the users last input so I that can set some of the form data default values based on their last entry]
Any help or reference to get me unstuck would be greatly appreciated.
Screenshot of console.log
Came up with a solution. Probably hacky but works.
First modify the getCollectionRt.js that runs the query as
...
const document = ref(null)
collectionRef = query(collectionRef, where(...q), orderBy(...ob), limit(...lmt))
const unsub = onSnapshot(collectionRef, snapshot => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ ...doc.data(), id: doc.id })
document.value = { ...doc.data(), id: doc.id }
})
// update values
documents.value = results
})
return { documents, document }
then pull in 'document' and return in vue as:
const { documents: lastEvent, document } = getCollectionRt(
'events',
['userId', '==', user.value.uid],
['created', 'desc'],
['1']
)
...
return {..., document }
then I can access it in the template as {{ document.id}}
Although this works, definitely spend more time learning about workign with object/arrays in VueJS

How to fetch all the documents with unique id from firestore database using React?

[Firestore SS][1]
[1]: https://i.stack.imgur.com/EI1Dm.png
I want to fetch each document as displayed in SS it's stored as Pets + unique_userId.
I am unable to fetch all data together. Just able to fetch one data of a particular user using the code below.
const [info,setInfo]=useState([]);
useEffect(() => {
db.collection("pets ESYXOPqlJpZ48np8LfNivnh9pvc2").onSnapshot((snapshot) =>
setInfo(snapshot.docs.map((doc) => doc.data()))
);
},[]);
Here ESYXOPqlJpZ48np8LfNivnh9pvc2 this is the userID of each unique user
Please help me out to fetch all the Pets data instead of hardcoding and fetching one particular data.
Try the following code,
const [docs, setDocs] = useState([]);
useEffect(() => {
const querySnapshot = await getDocs(collection(db,"pets ESYXOPqlJpZ48np8LfNivnh9pvc2"));
const document =[];
querySnapshot.forEach((doc) => {
document.push({
...doc.data(),
id: doc.id
});
});
setdocs(document);
}, []);
I'm guessing the appended id is a reference to the owner's id? In this case, would it be an option to fetch the owner list and use everyone's id to build a list of collection ids and then get all of their data?
If not, I only see to options:
Rethink your database structure - maybe use a unified pets collection and have a reference with/to that id in the pet documents.
Create a cloud function in which use #google-cloud/firestore to get the list of collections. There are tons of resources out there to help you get started with firebase cloud functions. Their documentation is pretty good also, and probably the most up-to-date
const functions = require('firebase-functions')
const { Firestore } = require('#google-cloud/firestore');
module.exports = functions
.region('europe-west3') // use the region you want here
.https.onRequest(async (request, response) => {
try {
const firestore = new Firestore();
const collections = (await firestore.listCollections()).map(collection => collection.id)
response.json({ data: collections })
} catch (error) {
response.status(500).send(error.message)
}
})
You'll get and endpoint which you can use to fetch the collection ids (e.g.: https://your-project-name.cloudfunctions.net/collections)
const [pets, setPets] = useState([]);
const [collectionIds, setCollectionIds] = useState([])
useEffect(() => {
fetch('https://your-project-name.cloudfunctions.net/collections')
.then(response => response.json())
.then(({ data }) => setCollectionIds(data))
}, [])
useEffect(() => {
collectionIds.forEach((collectionId) => {
// There are better ways to do this,
// I'm just using your approach so you can focus on the rest of the code
db.collection(collectionId).onSnapshot((snapshot) => {
setPets((currentPets) => [...currentPets, ...snapshot.docs.map((doc) => doc.data())])
})
})
}, [collectionIds])
Please note that these are very high-level implementations, there's no error handling, no teardowns or anything, so keep that in mind. Hope it helps, good luck!

Firestore freaking out

I just opened my project under another domain (the production url) and when I opened the network requests I saw this:
https://i.imgur.com/NxgTmIf.mp4
This took forever (8 min or more.) and my CPU was hot like hell, What did I do wrong?
My app is quite simple, I suspect the root of this is this block of code:
const [items, setItems] = useState([]);
const publish = async () => {
const batch = firestore.batch();
items.forEach(({ id }, index) => {
batch.update(firestore.collection('v1').doc(id), { '#': index });
});
await batch.commit();
};
const onCompletion = querySnapshot => {
const arr = [];
querySnapshot.forEach(document => {
const { vid: { id: vid }, '#': index } = document.data();
const { id } = document;
arr.push({ id, vid, index });
});
setItems(arr);
};
useEffect(() => {
const unsubscribe = firestore
.collection('v1')
.orderBy('#')
.onSnapshot(onCompletion);
return () => {
unsubscribe();
};
}, []);
useEffect(() => { publish(); }, [items]);
const handleSortEnd = ({ oldIndex, newIndex }) => {
if (oldIndex === newIndex) {
return;
}
setItems(arrayMove(items, oldIndex, newIndex));
};
Basically what this does is load a list of videos from a playlist on a firestore's collection then after the user add a new video or move up/down save again.
Any clues?
Edit: after this madness the app does few requests and works as expected.
There's multiple trips to loading your state I think, which by principle should be updated. Why not sort your incoming snapshot as desired before committing it to state. Give a condition to only do the sorting when 'items' has expected values.
Another way is 'setItems' as is from firestore and do sorting of data during the rendering of the state instead of hitting state before rendering.
Moreover, setting a new set of data to your state every time you sort it is mutation. React will take them as new data from before rendering which is not efficient.
If I follow this correctly, you've got a useEffect() that subscribes to changes to your 'v1' firestore collection. When the the component first loads and upon subsequent changes in the collection's documents, the onSnapshot event is triggered calling onCompletion. onCompletion then extracts all the documents from the collection to a new array which is then assigned to your items state. Your other useEffect() statement, triggers when items changes, calling the publish function which performs a batch update of documents in the same 'v1' collection ...which would trigger the onSnapshot again calling onCompletion and the cycle would repeat non-stop.
It might make more sense to retrieve the collection's documents once when the component loads and initialize the items state with the results then write the changed documents back to the collection when items is updated. You shouldn't need to retrieve the documents again with a collection subscription because you've already got them in your items array.

Firebase query and delete data in javascript

Here is my firebase
I would like to delete the last deed within deeds which happens to be deed id: 1. I would like to do this without specifying anything other than deleting the last deed in deeds.
Here is what I have tried already, but I receive no function errors because i'm returning query type objects.
const deedRef = admin.database().ref('/deeds');
deedRef.limitToLast(1).once("value", (snapshot) => {
snapshot.val().remove();
})
And
const deedRef = admin.database().ref('/deeds');
deedRef.limitToLast(1).once("value", (snapshot) => {
snapshot.forEach((deedSnapshot) =>{
deedSnapshot.remove();
})
})
And I've tried this
const deedRef = admin.database().ref('/deeds');
deedRef.limitToLast(1).remove();
How can I reference the last deed in deeds and remove it? The last deed will constantly change.
You were getting close:
const deedRef = admin.database().ref('/deeds');
deedRef.limitToLast(1).once("value", (snapshot) => {
snapshot.forEach((deedSnapshot) =>{
deedSnapshot.ref.remove();
})
})

Categories