What am I doing wrong to get an array from firebase - reactjs

I have this function in WorkoutController.js
export async function getFavourites(FavRetrived) {
var FavList = []
var snapshot = await firebase
.firestore()
.collection("Favourites")
.doc(firebase.auth().currentUser.uid)
.collection("userFavourites")
.get()
snapshot.forEach((doc) => {
const favDoc = doc.data()
favDoc.id = doc.id
FavList.push(favDoc)
})
FavRetrived(FavList)
}
I used basically flatlists to get the datas from the collection and it works good but now I want to use the id's array to do some controls.
I think the second part of the code it creates an array of id but I don't know how to use it or show it.
in Screen.js I have
import { getFavourites } from '../context/WorkoutController'
const [FavList, setFavList] = useState()
useEffect(() => {
getDataFav()
}, [])
function getDataFav() {
getFavourites(FavRetrieved)
}
function FavRetrieved(FavList) {
setFavList(FavList)
}
Like this if I use a flatlist passing FavList as data I can see all the elements of the collection, but what if I want to show just the first id of the array?
I don't want to use where function, I need to see the datas as an array, for example something like FavList[0].name (but this doesn't work)

First, we need to tweak getFavourites so that it handles the case where the user isn't logged in with a meaningful error and it shouldn't use callbacks if you are making use of async/await syntax - just use Promise chaining instead.
export async function getFavourites() {
const user = firebase.auth().currentUser;
if (!user)
return Promise.reject(new Error("User must be signed in first"));
const favList = []
const snapshot = await firebase
.firestore()
.collection("Favourites")
.doc(user.uid)
.collection("userFavourites")
.get()
snapshot.forEach((doc) => {
const favDocData = doc.data()
favDocData.id = doc.id
favList.push(favDocData)
})
return favList
}
You would then use it in your component like so:
import { getFavourites } from '../context/WorkoutController'
// status of favList
const [status, setStatus] = useState("loading")
// array of favourites
const [favList, setFavList] = useState()
// error message to show user
const [errorMsg, setErrorMsg] = useState("")
useEffect(() => {
let disposed = false
getFavourites()
.then((newFavList) => {
if (disposed) return // component discarded, do nothing.
setFavList(newFavList)
setErrorMsg("")
setStatus("fetched")
})
.catch((err) => {
if (disposed) return // component discarded, do nothing.
console.error("getFavourites failed: ", err)
setErrorMsg("Failed to get favourites")
setStatus("error")
});
// return cleanup function
return () => disposed = true
}, []);
if (status === "loading")
return (<Spinner />)
if (status === "error")
return (<div key="error">{errorMsg}</div>)
// if here, favList is now an array of favourite documents data
return ( /* ... render favList ... */ )

export async function getFavourites(FavRetrived) {
var FavList = []
var snapshot = await firebase
.firestore()
.collection("Favourites")
.doc(firebase.auth().currentUser.uid)
.collection("userFavourites")
.get()
snapshot.forEach((doc) => {
const favDoc = doc.data();
const id = doc.id;
FavList.push({ id, ...favDoc })
})
FavRetrived(FavList)
}
You can access it by FavList[0].id

Related

Push retrieved objects to state in React only brings me the last one

I'm fetching data from a json. And i want to display that data in my React component.But every time I try to pass the objects that I return from the json to my state, it returns only one and delete the previous, instead of the entire elements inside the json.
This is my code.
const [state, setState] = useState({});
const connection = new Connection("devnet");
const { publicKey } = useWallet();
useEffect(() => {
(async () => {
//if not public key, close
if(!publicKey) return;
//get tokens
let response = await connection.getTokenAccountsByOwner(
publicKey!, // owner here
{
programId: TOKEN_PROGRAM_ID,
}
);
response.value.forEach((e) => {
const accountInfo = SPLToken.AccountLayout.decode(e.account.data);
//get only tokens with value of 1
if ( parseInt(`${SPLToken.u64.fromBuffer(accountInfo.amount)}`) === 1 ) {
const tokenPublicKey = `${new PublicKey(accountInfo.mint)}`
//get the metadata of each NFT
const run = async () => {
const ownedMetadata = await programs.metadata.Metadata.load(connection, await programs.metadata.Metadata.getPDA(tokenPublicKey));
//get only tokens of the collection ...
if (ownedMetadata.data.updateAuthority === "Address_authority") {
//show the json data from arweave
let url= ownedMetadata.data.data.uri;
fetch(url)
.then(res => res.json())
.then((out) => {
setState(prevState => {
// THIS IS NOT WORKING FOR ME :(
return {...prevState, ...out};
});
})
.catch(err => { throw err });
}
};
run();
}
});
})()
}, [connection, publicKey]);
console.log(state)
{...prevState, ...out}; creates a new object, puts all of prevState's own properties on the new object, then puts all of out's own properties on the new object (overwriting the values from prevState if prevState also had properties with those names).
It sounds like you want an array, not a single object:
const [state, setState] = useState([]);
Then setting:
setState(prevState => [...prevState, out]);
Possibly unrelated, but that's potentially a bunch of distinct state changes (one for each element in response.value). Because the work is asynchronous, that could also result in a number of interim re-renders. Maybe you want that, but if you don't, you can do all the fetching and then update state once. Also, any time you're doing async work in a useEffect, you should allow for the possibility the effect's dependencies have changed in the meantime or the component has unmounted. Something like this (see *** comments):
const [state, setState] = useState({});
const connection = new Connection("devnet");
const { publicKey } = useWallet();
useEffect(() => {
// *** Use a controller to stop when the component unmounts, etc.
const controller = new AbortContoller();
const {signal} = controller;
(async () => {
if (signal.aborted) return; // ***
// If not public key, stop
if (!publicKey) return;
// Get tokens
let response = await connection.getTokenAccountsByOwner(
publicKey!, // owner here
{
programId: TOKEN_PROGRAM_ID,
}
);
// *** Build up the new data in this array (since we skip some elements,
// so we won't have a simple 1:1 mapping of `response.value` elements
// to result elements.
const newState = [];
// *** Wait for all the operations to finish and add their elements to `newState`
await Promise.all(
response.value.map(async (e) => {
const accountInfo = SPLToken.AccountLayout.decode(e.account.data);
// Skip tokens with value other than 1
if (parseInt(`${SPLToken.u64.fromBuffer(accountInfo.amount)}`) !== 1) {
return;
}
const tokenPublicKey = `${new PublicKey(accountInfo.mint)}`;
const ownedMetadata = await programs.metadata.Metadata.load(connection, await programs.metadata.Metadata.getPDA(tokenPublicKey));
// Get only tokens of the collection ...
if (ownedMetadata.data.updateAuthority !== "Address_authority") {
return;
}
// Show the data from arweave
let url = ownedMetadata.data.data.uri;
const response = await fetch(url, {signal}); // *** Pass the signal to `fetch`
if (!response.ok) { // *** This check was missing
throw new Error(`HTTP error ${response.status}`); // Or ignore if you prefer
}
const out = await response.json();
newState.push(out);
})
);
// *** Now we have all of them, do one state update
setState(prevState = [...prevState, ...newState]);
})();
return () => {
// Stop if our dependencies change or the component unmounts
controller.abort();
};
}, [connection, publicKey]);

Getting UNDEFINED values from Firestore onSnapshot + Promises for my React State

I'm trying to make a Tweets Application with React and Firebase and I have been suffering when trying to get info from more than 1 collection.
So this is the story:
I get the tweets using onSnapshot. All fine here
I need more info from 2 other collections: user_preferences and user_photo, so I use .get() within the onSnapshot
For managing asynchronism, I resolve my 2 promises before returning the tweet data + details data object for my map function.
I made a console.log of my mappedTweet and the values are OKEY. Here I can see the tweet data + details data
But my STATE "tweets" just have an array of undefined objects =(. It shows the right number of rows accoroding to my Tweets collection but rows of undefined data, and not the rows of my mappedTweets objects. Why?
Can anyone shed some light?
useEffect(() => {
//------------getting the TWEETS with onSnapshot()-------------
const cancelSuscription = firestore
.collection('tweets')
.onSnapshot((snapshot) => {
const promises = [];
const tweetsMapped = snapshot.docs.map((doc) => {
let tweetAndAuthor;
const tweetMappped = {
text: doc.data().text,
likes: doc.data().likes,
email: doc.data().email,
created: doc.data().created,
uid: doc.data().uid,
id: doc.id,
};
let authorPreference, authorPhoto;
const userPreferencePromise = firestore
.collection('user_preferences')
.where('uid', '==', tweetMappped.uid)
.get();
const userPhotoPromise = firestore
.collection('user_photos')
.where('id', '==', tweetMappped.uid)
.get();
promises.push(userPreferencePromise);
promises.push(userPhotoPromise);
//------------getting the AUTHOR USER PREFERENCES with .get()-------------
userPreferencePromise.then((snapshot2) => {
authorPreference = snapshot2.docs.map((doc) => {
return {
username: doc.data().username,
color: doc.data().color,
};
});
});
//------------getting the AUTHOR PHOTO with .get()-------------
userPhotoPromise.then((snapshot3) => {
authorPhoto = snapshot3.docs.map((doc) => {
return {
photoURL: doc.data().photoURL,
};
});
});
Promise.all(promises).then((x) => {
return {
...tweetMappped,
author: authorPreference[0].username,
authorColor: authorPreference[0].color,
authorPhoto: authorPhoto[0].photoURL,
};
});
});
Promise.all(promises).then((x) => {
setTweets(tweetsMapped);
});
});
return () => cancelSuscription();
}, []);
Well, I made it work by changing the model I was using to retrieve the data from Firebase.
I was using an outer onSnapshot with nested promises (I think I was very near here), but now I'm using nested onSnapshots and now app is behaving as expected.
So this is the new useEffect
useEffect(() => {
let cancelUserPrefSuscription, cancelUserPhotoSuscription;
// First onSnapshot
const cancelTweetSuscription = firestore
.collection('tweets')
.onSnapshot((tweetSnapshot) => {
const list = [];
tweetSnapshot.docs.forEach((tweetDoc) => {
//Second onSnapshot
cancelUserPrefSuscription = firestore
.collection('user_preferences')
.where('uid', '==', tweetDoc.data().uid)
.onSnapshot((userPrefSnapshot) => {
userPrefSnapshot.docs.forEach((userPrefDoc) => {
//Third onSnapshot
cancelUserPhotoSuscription = firestore
.collection('user_photos')
.where('id', '==', tweetDoc.data().uid)
.onSnapshot((userPhotoSnapshot) => {
userPhotoSnapshot.docs.forEach((userPhotoDoc) => {
//Taking the whole data i need from all snapshots
const newData = {
id: tweetDoc.id,
...tweetDoc.data(),
author: userPrefDoc.data().username,
authorColor: userPrefDoc.data().color,
authorPhoto: userPhotoDoc.data().photoURL,
};
list.push(newData);
//Updating my state
if (tweetSnapshot.docs.length === list.length) {
setTweets(list);
}
});
});
});
});
});
});
return () => {
cancelTweetSuscription();
cancelUserPrefSuscription();
cancelUserPhotoSuscription();
};
}, []);
Edit: Fix from comments of above code
Author: #samthecodingman
For each call to onSnapshot, you should keep track of its unsubscribe function and keep an array filled with the unsubscribe functions of any nested listeners. When an update is received, unsubscribe each nested listener, clear the array of nested unsubscribe functions and then insert each new nested listener into the array. For each onSnapshot listener attached, a single unsubscribe function should be created that cleans up the listener itself along with any nested listeners.
Note: Instead of using this approach, create a Tweet component that pulls the author's name and photo inside it.
useEffect(() => {
// helper function
const callIt = (unsub) => unsub();
// First onSnapshot
const tweetsNestedCancelListenerCallbacks = [];
const tweetsCancelListenerCallback = firestore
.collection('tweets')
.onSnapshot((tweetSnapshot) => {
const newTweets = [];
const expectedTweetCount = tweetSnapshot.docs.length;
// cancel nested subscriptions
tweetsNestedCancelListenerCallbacks.forEach(callIt);
// clear the array, but don't lose the reference
tweetsNestedCancelListenerCallbacks.length = 0;
tweetsNestedCancelListenerCallbacks.push(
...tweetSnapshot.docs
.map((tweetDoc) => { // (tweetDoc) => Unsubscribe
const tweetId = tweetDoc.id;
//Second onSnapshot
const userPrefNestedCancelListenerCallbacks = [];
const userPrefCancelListenerCallback = firestore
.collection('user_preferences')
.where('uid', '==', tweetDoc.data().uid)
.limitToFirst(1)
.onSnapshot((userPrefSnapshot) => {
const userPrefDoc = userPrefSnapshot.docs[0];
// cancel nested subscriptions
userPrefNestedCancelListenerCallbacks.forEach(callIt);
// clear the array, but don't lose the reference
userPrefNestedCancelListenerCallbacks.length = 0;
//Third onSnapshot
const userPhotoCancelListenerCallback = firestore
.collection('user_photos')
.where('id', '==', tweetDoc.data().uid)
.limitToFirst(1)
.onSnapshot((userPhotoSnapshot) => {
const userPhotoDoc = userPhotoSnapshot.docs[0];
// Taking the whole data I need from all snapshots
const newData = {
id: tweetId,
...tweetDoc.data(),
author: userPrefDoc.data().username,
authorColor: userPrefDoc.data().color,
authorPhoto: userPhotoDoc.data().photoURL,
};
const existingTweetObject = tweets.find(t => t.id === tweetId);
if (existingTweetObject) {
// merge in changes to existing tweet
Object.assign(existingTweetObject, newData);
if (expectedTweetCount === newTweets.length) {
setTweets([...newTweets]); // force rerender with new info
}
} else {
// fresh tweet
tweets.push(newData);
if (expectedTweetCount === newTweets.length) {
setTweets(newTweets); // trigger initial render
}
}
});
userPrefNestedCancelListenerCallbacks.push(userPhotoCancelListenerCallback);
});
// return an Unsubscribe callback for this listener and its nested listeners.
return () => {
userPrefCancelListenerCallback();
userPrefNestedCancelListenerCallbacks.forEach(callIt);
}
})
);
});
// return an Unsubscribe callback for this listener and its nested listeners.
return () => {
tweetsCancelListenerCallback();
tweetsNestedCancelListenerCallbacks.forEach(callIt);
};
}, []);
Edit: Splitting the code in two components
Note: Changed limitToFirst(1) --> limit(1). Splitting the fetch logic in two components simplified the onSnapshot approach!
1.The Parent Component
useEffect(() => {
const tweetsUnsubscribeCallback = firestore
.collection('tweets')
.onSnapshot((tweetSnapshot) => {
const mappedtweets = tweetSnapshot.docs.map((tweetDoc) => {
return {
id: tweetDoc.id,
...tweetDoc.data(),
};
});
setTweets(mappedtweets);
});
return () => tweetsUnsubscribeCallback();
}, []);
2.The Child Component: Tweet
useEffect(() => {
// Helper Function
const unSubscribe = (unsub) => unsub();
//------------getting the AUTHOR USER PREFERENCE
const userPrefNestedUnsubscribeCallbacks = [];
const userPrefUnsubscribeCallback = firestore
.collection('user_preferences')
.where('uid', '==', tweet.uid)
.limit(1)
.onSnapshot((userPrefSnapshot) => {
userPrefNestedUnsubscribeCallbacks.forEach(unSubscribe); // cancel nested subscriptions
userPrefNestedUnsubscribeCallbacks.length = 0; // clear the array, but don't lose the reference
//------------getting the AUTHOR PHOTO
const userPhotoUnsubscribeCallback = firestore
.collection('user_photos')
.where('id', '==', tweet.uid)
.limit(1)
.onSnapshot((userPhotoSnapshot) => {
// Taking the whole data I need from all snapshots
setAuthor({
author: userPrefSnapshot.docs[0].data().username,
authorColor: userPrefSnapshot.docs[0].data().color,
authorPhoto: userPhotoSnapshot.docs[0].data().photoURL,
});
});
userPrefNestedUnsubscribeCallbacks.push(userPhotoUnsubscribeCallback);
});
return () => {
userPrefUnsubscribeCallback();
userPrefNestedUnsubscribeCallbacks.forEach(unSubscribe);
};
}, []);
Basically, you've pushed the promises to your promise array in the state they were before you you processed their data. You want to make use of the Promise.all(docs.map((doc) => Promise<Result>)) pattern here where each document should return a single Promise containing its final result. This then means that the Promise.all will resolve with Result[].
Note: If inside a Promise you are mutating a variable outside of the Promise (e.g. pushing to an array), that is generally a sign that you are doing something wrong and you should rearrange your code.
Here's a quick example of throwing this together:
useEffect(() => {
let unsubscribed = false;
//------------getting the TWEETS with onSnapshot()-------------
const cancelSuscription = firestore
.collection('tweets')
.onSnapshot((snapshot) => {
const tweetsMappedPromises = snapshot.docs.map((doc) => {
let tweetAndAuthor;
const tweetMappped = {
text: doc.data().text,
likes: doc.data().likes,
email: doc.data().email,
created: doc.data().created,
uid: doc.data().uid,
id: doc.id,
};
//------------getting the AUTHOR USER PREFERENCES with .get()-------------
const userPreferencePromise = firestore
.collection('user_preferences')
.where('uid', '==', tweetMappped.uid)
.limitToFirst(1)
.get()
.then((prefDocQuerySnapshot) => {
const firstPrefDoc = photoDocQuerySnapshot.docs[0];
const { username, color } = firstPrefDoc.data();
return { username, color };
});
//------------getting the AUTHOR PHOTO with .get()-------------
const userPhotoPromise = firestore
.collection('user_photos')
.where('id', '==', tweetMappped.uid)
.limitToFirst(1)
.get()
.then((photoDocQuerySnapshot) => {
const firstPhotoDoc = photoDocQuerySnapshot.docs[0];
return firstPhotoDoc.get("photoURL");
});
//--------------------assemble this result---------------------
return Promises.all([userPreferencePromise, userPhotoPromise])
.then(([authorPreference, authorPhoto]) => {
return {
...tweetMappped,
author: authorPreference.username,
authorColor: authorPreference.color,
authorPhoto: authorPhoto.photoURL,
};
});
});
Promise.all(tweetsMappedPromises)
.then((tweetsMapped) => {
if (unsubscribed) return; // ignore result, dealing with out of date data
setTweets(tweetsMapped);
})
.catch((err) => {
if (unsubscribed) return; // ignore result, dealing with out of date data
// important! handle errors
});
});
return () => {
unsubscribed = true;
cancelSuscription();
}
}, []);
Notes:
You may benefit from using async/await syntax here instead.
On new onSnapshot calls, snapshot.docChanges() can be used to make it more efficient and speed up rerenders by only updating the entries that have changed (e.g. added/removed/modified). You would use setTweets(previousTweetsMapped => /* newTweetsMapped */) for this.

How to get a single document from firestore?

According to the documentation from firebase you can get a document very simply by using get()
But for some reason in my code it always displays that there's no such document, even though it does exist, this is what I'm doing:
useEffect(() => {
console.log(user, "This is the user UID:"+user.uid)
const userDoc = db.collection('usuarios').doc(user.uid);
const doc = userDoc.get();
if (!doc.exists) {
console.log('No such document!');
}
else {
userDoc
.onSnapshot(snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setUserData(tempData);
})
}
}, [user]);
This is what the console.log() shows:
This is how it looks in firebase:
const doc = userDoc.get();
if (!doc.exists) {
.get returns a promise, so you're checking the .exists property on a promise, which is undefined. You will need to wait for that promise to resolve, either with .then:
userDoc.get().then(doc => {
if (!doc.exists) {
// etc
}
});
Or by putting your code in an async function and awaiting the promise:
const doc = await userDoc.get();
if (!doc.exists) {
// etc
}
If you're using the firebase 8 web version, the userDoc.get() returns a promise, not the document:
userDoc.get().then((doc) => {
if (!doc.exists) {
console.log('No such document!');
} else {
const tempData = [];
const data = doc.data();
tempData.push(data);
setUserData(tempData)
console.log('it worked')
}
}).catch((error) => {
console.log("Error getting document:", error);
});
You can get more info about promises in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises.
In your code you are using the get method to fetch user data and get doesn't provide a snapshot. also, you missed that get() will return a promise so you have to handle using async-await or .then etc.
useEffect(() => {
console.log(user, "This is the user UID:"+user.uid);
getUser(user.uid).then(userData => {
setUserData(userData);
});
}, [user]);
const getUser = async (id) => {
try {
const user = await db.collection('usuarios').doc(id).get();
const userData = user.data();
return userData;
} catch (err){
console.log('Error during get user, No such document!');
return false;
}

Problem occur when use array of objects in filter function React

I try to get data from the backend and view data in the frontend. To do this I try this code.
function ViewPost() {
const { postId } = useParams();
console.log(postId);
const [posts, setPosts] = useState({});
useEffect(() => {
getOnePost();
}, []);
const getOnePost = async () => {
try {
const response = await axios.get(`/buyerGetOnePost/${postId}`);
console.log(response);
const allPost = response.data.onePost;
setPosts(allPost);
} catch (error) {
console.error(`Error: ${error}`);
}
};
console.log(posts);
console.log(posts.wasteItemList);
const [offers, getOffers] = useState([]);
useEffect(() => {
getAllOffers();
}, []);
const getAllOffers = async () => {
await axios
.get(`/viewPendingSellerOffers`)
.then((response) => {
const allNotes = response.data.existingOffers;
getOffers(allNotes);
})
.catch((error) => console.error(`Error: ${error}`));
};
console.log(offers);
const wasteItem = offers?.filter(
(wasteItem) =>
wasteItem.status === "accepted" &&
wasteItem.wasteItemsListId === posts?.wasteItemList?._id,
);
console.log(wasteItem);
}
I call the first API and get a specific post data and this post has an array of objects called wasteItemList. When I use this code console.log(posts.wasteItemList), I get length 2 array of objects. This is an image of this result.
Then I call the second API and get length 8 array of objects. This is an image of this result.
Then I try to filter data using this code const wasteItem = offers?.filter(wasteItem => wasteItem.status==='accepted' && wasteItem.wasteItemsListId===posts?.wasteItemList?._id). But this filter function give an empty array. What is the reason for this problem? How do I solve this?
As I mentioned in the comments, your naming seems a little off. A function that supposedly gets one post assigns to a state atom that's plural, and your setter for offers is getOffers.
Here's a simplification/rewrite of your component that assumes post is supposed to be singular and offers is in plural. Also, you were missing the data dependency postId for the useEffect.
In addition, since wasteItem is singular, I assume you want the first matching offer, not all of them, so .find() is the thing.
function ViewPost() {
const { postId } = useParams();
const [post, setPost] = useState(undefined);
const [offers, setOffers] = useState(undefined);
useEffect(() => {
setPost(undefined);
axios
.get(`/buyerGetOnePost/${postId}`)
.then((resp) => setPost(resp.data.onePost))
.catch((err) => console.error(err));
}, [postId]);
useEffect(() => {
axios
.get(`/viewPendingSellerOffers`)
.then((response) => setOffers(response.data.existingOffers))
.catch((err) => console.error(err));
}, []);
if (post === undefined || offers === undefined) {
return <>Loading...</>;
}
const wasteItem = offers.find(
(wasteItem) =>
wasteItem.status === "accepted" &&
wasteItem.wasteItemsListId === post.wasteItemList?._id,
);
return (
<div>
<div>Post: {JSON.stringify(post)}</div>
<div>Offers: {JSON.stringify(offers)}</div>
<div>Waste Item: {JSON.stringify(wasteItem)}</div>
</div>
);
}

Update state with Object using React Hooks

I'm getting data from Firebase and want to update state:
const [allProfile, setAllProfile] = useState([]);
.....
const displayProfileList = async () => {
try {
await profile
.get()
.then(querySnapshot => {
querySnapshot.docs.map(doc => {
const documentId = doc.id;
const nProfile = { id: documentId, doc: doc.data()}
console.log(nProfile);//nProfile contains data
setAllProfile([...allProfile, nProfile]);
console.log(allProfile); // is empty
}
);
})
} catch (error) {
console.log('xxx', error);
}
}
The setAllProfile will update the state when the iteration is done. So in order for your code to work, you will need to pass the callback function to the setAllProfile as shown in the docs
setAllProfile((prevState) => [...prevState, nProfile])
UPDATE
Example demonstrating this at work
Since setAllProfile is the asynchronous method, you can't get the updated value immediately after setAllProfile. You should get it inside useEffect with adding a allProfile dependency.
setAllProfile([...allProfile, nProfile]);
console.log(allProfile); // Old `allProfile` value will be printed, which is the initial empty array.
useEffect(() => {
console.log(allProfile);
}, [allProfile]);
UPDATE
const [allProfile, setAllProfile] = useState([]);
.....
const displayProfileList = async () => {
try {
await profile
.get()
.then(querySnapshot => {
const profiles = [];
querySnapshot.docs.map(doc => {
const documentId = doc.id;
const nProfile = { id: documentId, doc: doc.data()}
console.log(nProfile);//nProfile contains data
profiles.push(nProfile);
}
);
setAllProfile([...allProfile, ...profiles]);
})
} catch (error) {
console.log('xxx', error);
}
}
You are calling setState inside a map and therefore create few async calls, all referred to by current ..allProfile value call (and not prev => [...prev...)
Try
let arr=[]
querySnapshot.docs.map(doc => {
arr.push({ id: doc.id, doc: doc.data() })
}
setAllProfile(prev=>[...prev, ...arr])
I don't sure how the architecture of fetching the posts implemented (in terms of pagination and so on, so you might don't need to destruct ...prev

Resources