I have two files contact.js
and functions.js
. I am using firestore realtime functionality.
Here is my functions.js file code:
export const getUserContacts = () => {
const contactDetailsArr = [];
return db.collection("users").doc(userId)
.onSnapshot(docs => {
const contactsObject = docs.data().contacts;
for (let contact in contactsObject) {
db.collection("users").doc(contact).get()
.then(userDetail => {
contactDetailsArr.push({
userId: contact,
lastMessage: contactsObject[contact].lastMsg,
time: contactsObject[contact].lastMsgTime,
userName:userDetail.data().userName,
email: userDetail.data().emailId,
active: userDetail.data().active,
img: userDetail.data().imageUrl,
unreadMsg:contactsObject[contact].unreadMsg
})
})
}
console.log(contactDetailsArr);
return contactDetailsArr;
})
}
in contact.js
when I do:
useEffect(() => {
let temp = getUserContacts();
console.log(temp);
}, [])
I want to extract data of contactDetailsArr
in contacts.js
but I get the value of temp consoled as:
ƒ () {
i.Zl(), r.cs.ws(function () {
return Pr(r.q_, o);
});
}
How do I extract the array data in my case?
3
Answers
The
onSnapshot()
returns a function that can be used to detach the Firestore listener. When using a listener, it’s best to set the data directly into state rather than returning something from that function. Try refactoring the code as shown below:Then use
contacts
array to map data in to UI directly.Your code seems to be not written with async/await or promise like style
e.g. contactDetailsArr will be returned as empty array
also onSnapshot creates long term subscription to Firestore collection and could be replaced with simple get()
See example on firestore https://firebase.google.com/docs/firestore/query-data/get-data#web-version-9_1
Assumptions
This answer assumes a user’s data looks like this in your Firestore:
Note: When asking questions in the future, please add examples of your data structure similar to the above
Attaching Listeners with Current Structure
The structure as shown above has a number of flaws. The "contacts" object in the user’s data should be moved to a sub-collection of the user’s main document. The reasons for this include:
active
,email
,imageUrl
, anduserName
propertiesTo fetch a user’s contacts once in your
functions.js
library, you would use:Example Usage:
To fetch a user’s contacts, and keep the list updated, using a function in your
functions.js
library, you would use:Note: The above code will not (due to complexity):
setUserContactsData
method is called out of order due to network issuesdb
instance is changed on every renderExample Usage:
Attaching Listeners with Sub-collection Structure
To restructure your data for efficiency, your structure would be updated to the following:
Using the above structure provides the following benefits:
To fetch a user’s contacts once in your
functions.js
library, you would use:Example Usage:
To fetch a user’s contacts in a way where it’s kept up to date, we first need to introduce a couple of utility
useEffect
wrappers (there are libraries for more robust implementations):To use that method to hydrate a contact, you would call it from a
ContactEntry
component:Those
ContactEntry
components would be populated from aContacts
component:Example Usage: