Here is how my savedLinksData array prints in my console:
Here is my code that attempts to get the linkType value:
{savedLinksData.map((saved) => {
return <h1>{saved.linkType}</h1>;
})}
What I'm doing wrong?
I think there may be something wrong with the way I stored the values in the array. It doesn't look like the value are inside of the array.
Picture of console.log("----->", saved)
const [savedLinksData, setSavedLinksData] = useState([]);
// query for saved links data
useEffect(() => {
if (user) {
async function fetchData() {
const request = await db
.collection("users")
.doc(user)
.collection("saved")
.onSnapshot((snapshot) =>
setSavedLinks(
snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
)
);
}
fetchData();
} else {
setSavedLinks([]);
}
}, [user]);
useEffect(() => {
if (savedLinks.length > 0) {
let newArray = [];
savedLinks.map((saved) => {
db.collection("users")
.doc(saved.savedUser)
.collection("links")
.doc(saved.savedLinkId)
.get()
.then(function (doc) {
if (doc.exists) {
// console.log("Document data:", doc.data());
newArray.push(doc.data());
// setSavedLinksData([...savedLinksData, doc.data()]);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function (error) {
console.log("Error getting document:", error);
});
});
setSavedLinksData(newArray);
}
}, [savedLinks]);
Related
I have a promise to get a firebase collection and then return respective data (all according firebase documentation).
The data is returning fine, the only problem is that it seems that I am unable to set the collection data to a state array. When console.log(chats)this returns an empty array.
The data is returning fine as adding a console.log(doc.data()) in the first then logs the data ok but then it is empty.
What am I doing wrong in the promise?
const HomeScreen: React.FC<Props> = ({ navigation}) => {
const [chats, setChats] = useState<{[key: string]: any}>([]);
useEffect(() => {
const chatsArray = [{}];
db.collection("chats")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
chatsArray.push({
id: doc.id,
data:doc.data(),
})
});
})
.then(() => {
setChats(chatsArray)
console.log(chats);
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
}, []);
return (
<SafeAreaView>
<ScrollView style={styles.container}>
{chats go here}
</ScrollView>
</SafeAreaView>
)
}
export default HomeScreen;
The console.log(chats); doesn't work, since updating the state is a asynchronous operation that won't have completed by the time you log the value. Try console.log(chatsArray); to see the actual value.
In addition, consider passing the array as the result along the promise chain instead of using a variable in the context:
db.collection("chats")
.get()
.then((querySnapshot) => {
return querySnapshot.docs.map((doc) => {
return {
id: doc.id,
data:doc.data(),
}
});
})
.then((chatsArray) => {
setChats(chatsArray)
console.log(chatsArray);
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
Or simpler and with the same result:
db.collection("chats")
.get()
.then((querySnapshot) => {
const chatsArray = querySnapshot.docs.map((doc) => {
chatsArray.push({
id: doc.id,
data:doc.data(),
})
});
setChats(chatsArray)
console.log(chatsArray);
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
I have this on my firestore:
and this is how I'm retrieving the data. I simply copied it from the firebase documentation and it keeps on saying "No such document"
useEffect(() => {
const Ref = firestore.collection("business").doc("business-store");
const doc = Ref.get();
if (!doc.exists) {
console.log("No such document!");
} else {
console.log("Document data:.", doc.data());
}
}, []);
You can try in this way, it should give you the result:
useEffect(() => {
firestore.collection("business").doc("business-store").get().then(doc=>{
if (!doc.exists) {
console.log("No such document!");
} else {
console.log("Document data:.", doc.data());
}
}).catch(error={})
}, []);
I have the following useEffect set up to fetch some data from firebase to populate a flatlist
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
querySnapshot.forEach((doc) => {
const { sensorReadingsId } = doc.data();
// This works, but doesn't load the additional data I need.
// list.push({ ...{ id: doc.id }, ...doc.data() });
// This doesn't seem to load anything.
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
list.push({ ...{ id: doc.id }, ...doc.data(), ...documentSnapshot.data() });
});
});
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
});
}, [currentAccountId, currentShopId]);
Here's the problem: my flatlist doesn't load anything. When I initially load the screen, that console.log outputs an empty array. If I force it to re-render by saving the screen file, the array is correctly populated.
As noted in that comment, if I just use list.push({ ...{ id: doc.id }, ...doc.data() }); instead of db.collection('sensors')... it loads the sensors fine, but without the additional data that I need.
How do I resolve this? Thanks in advance!
Solution: As #Rajitha Udayanga has pointed out, checking the length of the first querysnapshot against the list array is a great way to check whether or not all the data has been loaded. Here's the working code (note the conditional that setSensorsData has been moved into):
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
querySnapshot.forEach((doc) => {
const { sensorReadingsId } = doc.data();
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
list.push({ ...{ id: doc.id }, ...doc.data(), ...documentSnapshot.data() });
// Fix is here:
if (querySnapshot.docs.length === list.length) {
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
}
});
});
});
}, [currentAccountId, currentShopId]);
Disclaimer: I have zero experience with React.
In Angular I deal with such issues using RxJS's combineLatest.
However, you can try this one:
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
const useValues = () => {
if (list.some(value => value === undefined)) {
return; // not everything has loaded yet - abort
}
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
}
querySnapshot.forEach((doc, index) => {
list.push(undefined);
const { sensorReadingsId } = doc.data();
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
list[index] = ({ id: doc.id, ...doc.data(), ...documentSnapshot.data() });
useValues();
});
});
});
}, [currentAccountId, currentShopId]);
And don't forget to detach the listeners when you don't need them anymore.
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
querySnapshot.forEach((doc) => {
const { sensorReadingsId } = doc.data();
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
const newData = {
...{ id: doc.id },
...doc.data(),
...documentSnapshot.data()
};
const itemIndex = list.findIndex(item => item.id ===
newData.id);
if (itemIndex !== -1) {
list[itemIndex] = newData;
} else {
list.push(newData);
}
// do it like thi
if (querySnapshot.docs.length === list.length) {
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
}
});
});
});
}, [currentAccountId, currentShopId]);
PROBLEM: I need to access the linkTitle values for each object in my array.
Here is how my savedLinksData array is structured:
And here is my code:
{savedLinksData.map((saved, key) => (
<h1>{saved.linkType}</h1>
))}
Full Code:
// query for saved links data
useEffect(() => {
if (user) {
async function fetchData() {
const request = await db
.collection("users")
.doc(user)
.collection("saved")
.onSnapshot((snapshot) =>
setSavedLinks(
snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
)
);
}
fetchData();
} else {
setSavedLinks([]);
}
}, [user]);
useEffect(() => {
if (savedLinks.length > 0) {
let newArray = [];
savedLinks.map((saved) => {
db.collection("users")
.doc(saved.savedUser)
.collection("links")
.doc(saved.savedLinkId)
.get()
.then(function (doc) {
if (doc.exists) {
// console.log("Document data:", doc.data());
newArray.push(doc.data());
// setSavedLinksData([...savedLinksData, doc.data()]);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function (error) {
console.log("Error getting document:", error);
});
});
setSavedLinksData(newArray);
}
}, [savedLinks]);
console.log("RETURNED LINKS DATA", savedLinksData);
console.log("SAVED", savedLinks);
You can do {savedLinksData.map((saved, key) => ( <h1>{saved.linkInfo.linkTitle}</h1> ))}
Ok so i use the useEffect to get firebase firestore data. This is my hook code:
const [campaign, setCampaigns] = useState([])
useEffect(() => {
var docRef = db.collection("campaigns").doc(slug);
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
setCampaigns(doc.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
},[]);
console.log(campaign[0].startDate.seconds) // gives the error stated below
console.log(campaign.images[0].url)// gives the error stated below
The response is:
I can get access to all the top level properties such as body, campaignPolicy etc. but if I do
TypeError: Cannot read property 'seconds' of undefined
If I do campaign.startDate.seconds i get same error:
TypeError: Cannot read property 'seconds' of undefined
Full minimal code:
const ReviewComponent = (props) => {
const [open, setOpen] = useState(false)
const [campaigns, setCampaigns] = useState([])
const [currentCampaign, setCurrentCampaign] = useState([])
//First useEffect hook
const tempDoc = []
useEffect(() => {
var docRef = db.collection("campaigns");
docRef.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
tempDoc.push({ id: doc.id, ...doc.data() })
// doc.data() is never undefined for query doc snapshots
setCampaigns(tempDoc)
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
},[]);
//Second use effect hook
useEffect(() => {
const docRef = db.collection("campaigns").doc(slug);
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
setCurrentCampaign(doc.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
},[]);
const handleOpen = () => {
setOpen(true)
};
const handleClose = () => {
setOpen(false);
};
if(currentCampaign[0]) { // Or if(campaign[0])
console.log(currentCampaign)
}
return(
<Typography>
{currentCampaign.reviews[0].text}
</Typography>
)
You are trying to access your data before they are retrieved. Here is what happens:
// First render
You set your state as an empty array
You declare an effect with a callback that will be called after the first render
You try to access a value that is indeed undefined (your array is empty at this point) and your app crash
// Second render
Your code crash at the first render but if it didn't:
Your state now contains your data
Your effect will not be called as you have an empty dependency array
You can use your data
To make your code work, and you should do it every time you load data asynchronously, is to check that your data are loaded.
const [campaign, setCampaigns] = useState([]);
useEffect(() => {
const docRef = db.collection("campaigns").doc(slug);
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
setCampaigns(doc.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
},[]);
if(campaign.length > 0) { // Or if(campaign[0])
console.log(campaign[0].startDate.seconds)
console.log(campaign[0].images[0].url)
}
The default value of campaign is empty array and it is set after few time from firebase store.
So you should check campaign state what is empty or has value.
if (campaign.length == 0)
console.log(campaign[0].startDate.seconds);