async function getData(ret) {
var list = [];
const b = await firebase.database().ref('tesco').limitToFirst(20).on('value',function (snapshot) {
snapshot.forEach(function (childSnapshot) {
list.push(childSnapshot.val())
I have to push data from firebase , to Array. It takes alot of time and i have to refresh App to see data on Screen.
put the list or return of getData() in a state, so every time it has new value the component will refreshed automatically
Related
I'm writing a table component for my page in React.
I have a function loadData() that makes a request to an api. Using the api result to update the data state variable, using a new reference.
The problem here is that React doesn't trigger any re-render for the data variable.
const [data, setData] = useState([]);
const loadData = async () => {
try {
...
let response_json = await response.json();
setData(transformData(response_json.items));
...
}
const transformData = (data) => {
if (data === undefined || data === null) {
return [];
}
let new_data = [];
data.forEach((entry,index) => {
new_data.push(cloneElement(props.config.table_entry,{data:entry, key:index}, null));
});
return new_data;
}
I am using this code to change the table's page, making a request with parameters like pageNumber, pageSize, filters. So even with different data and different reference, still it doesn't trigger re-rendering.
This problem has challenged me for like one whole morning was that the data variable continued to updated on every request made but the webpage never re-rendered.
The answer lies here
data.forEach((entry,index) => {
new_data.push(cloneElement(props.config.table_entry,{data:entry, key:index}, null));
});
in the transformData function where it creates a new array of new data, BUT the key property of the component never changed because it was the index of its position in the array returned from the server.
Assigning the key to a unique id solved the problem.
I want to retrieve the data from donation and display it in a table. I was able to retrieve the user data from Users and displayed it on a table. But now I don't know how I will be able to retrieve the data from donation.
This is my database structure in Firebase. Note: All of the data that was entered came from a mobile app created in Android Studio.
This is the code that I made when retrieving the User data.
function AddAllITemsToTable(User) {
id=0;
tbody.innerHTML="";
User.forEach(element => {
AddItemToTable(element.uid, element.fullName, element.organization, element.contactPerson, element.contactNo, element.location, element.emailAddress, element.status);
});
}
function GetAllDataRealtime() {
const dbRef = ref(database, 'Users');
onValue(dbRef,(snapshot) => {
var Users = [];
snapshot.forEach(childSnapshot => {
Users.push(childSnapshot.val());
});
AddAllITemsToTable(Users);
})
}
window.onload = GetAllDataRealtime;
Since you're calling onValue on /Users, you already get all data for all users and all of their donations. To process the donations in your code:
const dbRef = ref(database, 'Users');
onValue(dbRef,(snapshot) => {
var Users = [];
snapshot.forEach(userSnapshot => {
Users.push(userSnapshot.val());
userSnapshot.child("donation").forEach((donationSnapshot) => {
console.log(donationSnapshot.key, donationSnapshot.val());
});
});
AddAllITemsToTable(Users);
})
As I said in my comment, I recommend reading the Firebase documentation on structuring data, as the way you nest donations under each user does not follow the guidance on nesting data and keeping your structure flat.
I'm creating a react js app for a project that interacts with an api. I get an array with values from the api, and those values are google maps pins. What I want to do here is: the user sets a data interval and all the pins in that data interval are shown in the map.
I have this js code to set everything:
//above is the function that receives two dates and returns the interval, not relevant here
var pinsdata = [];
useEffect(()=>{
axios.get('MYAPILINK')
.then(res=>{
var arraydatas = getDates(data1, data2)
for(var i = 0; i < res.data.data.length; i++)
{
if(arraydatas.includes(res.data.data[i].data_pg))
{
pinsdata.push(res.data.data[i])
}
}
setPorra(pinsdata)
})
}, [porra])
and for now i just have this on the return (I'm just testing this for now):
<div>
<DatePicker selected={startDate} onSelect={(date) => setStartDate(date)} dateFormat="dd-MM-y"/>
<DatePicker selected={startDate2} onSelect={(date2) => setStartDate2(date2)} dateFormat="dd-MM-y"/>
<ul>
{porra.map(pinsdata =>{
return(<li>{pinsdata.id_pg}</li>)
})}
</ul>
</div>
So... everything seems to work fine here: I set two dates and I get all the markers/pins in that data interval shown on the page.
The problem:
My page is INFINITELY re-rendering... The console.log that i have there gets spammed as soon as i refresh the page, and when i add the google map to the page, it's not even usable because the page just get's re-render every second. Now... i know that it's because of useState, but how can i get this going without use state? Because the api request is async, so i have to re-render when i have the answer from the api...
You dependencies array is incorrect you should have data1 and data2 in there instead -
var pinsdata = [];
useEffect(()=>{
axios.get('MYAPILINK')
.then(res=>{
var arraydatas = getDates(data1, data2)
for(var i = 0; i < res.data.data.length; i++)
{
if(arraydatas.includes(res.data.data[i].data_pg))
{
pinsdata.push(res.data.data[i])
}
}
setPorra(pinsdata)
})
}, [data1, data2])
providing that you want to run this effect when data1 or data2 changes, otherwise leave the array blank, and it will only run once, when the component mounts
Try removing porra from the dependancy array and add two dates(startDate and startDate2) as the dependencies.
useEffect(()=>{
axios.get('MYAPILINK')
.then(res=>{
var arraydatas = getDates(data1, data2)
for(var i = 0; i < res.data.data.length; i++)
{
if(arraydatas.includes(res.data.data[i].data_pg))
{
pinsdata.push(res.data.data[i])
}
}
setPorra(pinsdata)
})
}, [startDate, startDate2])
when you call setPorra inside useEffect, it creates an infinite loop.
I am making a get request to an API that is linked to my database.
dataApi is a really big object with a lot of objects and arrays nested within it.
Some of the entries from the database are not having the full details that I need so I am filtering them to only show those with a length of > 5.
Now the issue is when I try to get the name of each entry which is split into either Tag1, Tag2 or Tag3.
Before this when I was accessing all the entries and getting the items within them there was no issue.
But when I try to filter them by the name and store the objects corresponding to that name in its state this issue arrises.
Edit:
When I console.log(arr1) it shows all the data but the moment I set the state to it it causes the error.
// Data from all entries in database
const [dataApi, setDataApi] = useState();
// Data for each of the tags
const [tag1, setTag1] = useState();
const [tag2, setTag2] = useState();
const [tag3, setTag3] = useState();
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
setDataApi(data);
});
}, []);
const getTagDetails = data => {
const arr1 = [];
const arr2 = [];
const arr3 = [];
data &&
data.forEach(d => {
// Entries into the database which do not have any tag information
// have a size of 5 and those with all the details have a size of 6
const sizeOfObject = Object.keys(d).length;
// Only need database items with all the details
if (sizeOfObject > 5) {
const name = d["tags"]["L"][0]["M"]["name"]["S"];
// Split the data for the tags into their respective name
// Will be used to set individual datasets for each tag
if (name === "Tag1") {
arr1.push(d);
}
if (name === "Tag2") {
arr2.push(d);
}
if (name === "Tag3") {
arr3.push(d);
}
}
});
setTag1(arr1);
setTag2(arr2);
setTag3(arr3);
};
getTagDetails(dataApi);
I guess the problem is you call getTagDetails(dataApi) inside of file so it causes this infinite rendering problem
Instead remove getTagDetails and try to call this functions after API resolved.
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
getTagDetails(data)
});
}, []);
I think your problem is the way you have structured your getTagDetails function. Each time you render, you call getTagDetails() and the first thing you do is create a new array for each tag. When you call setTag with the new array, it will rerender. You'll probably want to move the getTagDetails logic into the effect so it only runs once on mount (or add a dependency to the dependency array if you need to update on new data)
This is my first project with Firebase, and I've created a relational data structure. Now I see why this isn't the best way to do things!
In this part of my app, users can add multiple items to an outfit - here's a diagram of the data structure/relationship I have in Firebase now.
I've included code from a Redux action creator in my React Native app. When a user edits an outfit - removing some items - this code:
takes an array with the new list of items from the client
compares this array with the current saved items server side
creates a new array of the diff (removed items)
loops through the outfits for each of those items, matching against the outfit being edited
removes references that match
This code works, but is pretty deeply nested and messy:
export const updateTagReferences = (localTags, outfitId) => {
//localTags represents the new set of items from application state
//outfitId is the uid for the outfit where those items appear
const {currentUser} = firebase.auth();
return dispatch => {
firebase
.database()
.ref(`users/${currentUser.uid}/outfits/${outfitId}/taggedItems`)
.once('value')
.then(snapshot => {
var serverTags = snapshot.val();
// Work out the diff (ie, which items have been removed locally)
return _.differenceWith(serverTags, localTags, _.isEqual);
})
.then(toDelete => {
toDelete.map(item => {
firebase
.database()
.ref(`users/${currentUser.uid}/items/${item.item.uid}/outfits`)
.once('value')
.then(snapshot => {
var outfits = snapshot.val();
for (var taggedItem in outfits) {
if (outfits.hasOwnProperty(taggedItem)) {
var i = outfits[taggedItem];
return i.uid === outfitId
? firebase
.database()
.ref(
`users/${currentUser.uid}/items/${item.item.uid}/outfits/${taggedItem}`,
)
.remove()
: null;
}
}
});
});
})
.then(console.log('Done'));
};
};
As you can see, I'm trying to use Firebase promises to loop through the outfits nested in each item server side.
My issues are:
I'm making lots of queries to Firebase, which isn't ideal
console.log('Done') fires before .remove() - I need to follow up with a second action once this action completes
I've tried the following code, based on this answer, but I can't seem to get it to work:
export const updateTagReferences = (localTags, outfitId) => {
const {currentUser} = firebase.auth();
return dispatch => {
// Create a ref for the outfit the user is editing
firebase
.database()
.ref(`users/${currentUser.uid}/outfits/${outfitId}/taggedItems`)
.once('value')
.then(snapshot => {
// Compare local (app state) with remote (Firebase) to work out which items have been removed
var serverTags = snapshot.val();
return _.differenceWith(serverTags, localTags, _.isEqual);
})
.then(toDelete => {
var promises = [];
// Create a ref for each item to delete
toDelete.map(item => {
firebase
.database()
.ref(`users/${currentUser.uid}/items/${item.item.uid}/outfits`)
.once('value')
.then(snapshot => {
var outfits = snapshot.val();
for (var taggedItem in outfits) {
// Loop through the outfits that item appears in
if (outfits.hasOwnProperty(taggedItem)) {
var i = outfits[taggedItem];
// Match against the outfit the user is editing
return i.uid === outfitId
? promises.push(
firebase
.database()
.ref(
`users/${currentUser.uid}/items/${item.item.uid}/outfits/${taggedItem}`,
)
// Remove the reference
.remove(),
)
: null;
}
}
})
// Execute all the promises, removing firebase references for each item removed locally
.then(Promise.all(promises).then(console.log('Done')));
});
});
};
};
I'm trying to make my code more efficient overall, so I can replicate it elsewhere in my project. I'd love an explanation of the best way to move this kind of query server side.
Currently, changing the whole data structure is out of scope (that's for next time!)
Thanks.