Reputation: 15
const CollectionPage = () => {
const currentPath = usePathname().substring((usePathname().lastIndexOf("/")
) + 1)
const colPath = currentPath
// console.log(colPath)
const [details, setDetails] = useState()
const [list, setList] = useState([])
// const docRef = doc(db, 'users', 'user1');
// const colNameRef = collection(db, 'users', 'user1', 'colName');
const animeListRef = collection(db, 'users', 'user1', 'animeList');
const q = query(animeListRef, where("colName", "==", colPath));
useEffect(() => {
const getId = async () => {
onSnapshot(q, (querySnapshot) => {
let animes = [];
querySnapshot.forEach((doc) => {
animebyId(({ ...doc.data(), id: doc.id }.animeId)).then((response) => animes.push(response.media[0]));
});
setList(animes);
// console.log(list)
})
}
getId()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
console.log(list)
// console.log(list.length)
return (
<>
{list === undefined ?
<>still Loading.....</> :
<>
<p>list</p>
{/* <ImageList sx={{ width: '100%', height: '100%' }}>
<ImageListItem key="Subheader" cols={2}>
<ListSubheader component="div">{colPath}</ListSubheader>
</ImageListItem>
{list.map(item => {
return (
<>
<ImageListItem key={item.id}>
<img
src={`${item.coverImage.extraLarge}?w=248&fit=crop&auto=format`}
srcSet={`${item.coverImage.extraLarge}?w=248&fit=crop&auto=format&dpr=2 2x`}
alt={item.title.english ? item.title.english : item.title.romaji}
loading="lazy"
/>
<ImageListItemBar
title={item.title.english ? item.title.english : item.title.romaji}
subtitle={item.author}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info about ${item.title.english ? item.title.english : item.title.romaji}`}
>
<InfoIcon />
</IconButton>
}
/>
</ImageListItem>
</>
)
})}
</ImageList> */}
</>
}
</>
)
}
export default CollectionPage
as we can see on this console there are arrays, but whenever use map in jsx I never render this array.
Let's straight:
UPDATE
This is my animeById function
async function animebyId(id, page = 1, perPage = 1) {
const query = `
query ($id: Int, $page: Int, $perPage: Int) {
Page(page: $page, perPage: $perPage) {
media(id: $id, type: ANIME) {
id
title {
romaji
english
}
siteUrl
coverImage {
extraLarge
}
bannerImage
description(asHtml: false)
status
season
seasonYear
startDate {
year
month
day
}
duration
source
type
genres
averageScore
meanScore
}
}
}`;
let variables = {
page: page,
perPage: perPage,
id: id
};
const headers = {
"Content-Type": "application/json",
Accept: "application/json",
};
return await axios.post(`https://graphql.anilist.co`, {
query,
variables,
headers
}).then(response => {
// console.log('api response entry', response.data.data.Page)
return response.data.data.Page
}).catch((err) => console.log(err.message))
}
Upvotes: 1
Views: 649
Reputation: 4623
This is how you should do it:
const getId = async () => {
// Snapshoot returns Unsubscribe to prevent from memoryleaks you need to ivoke it later like unsub()
const unsub = onSnapshot(q, async (querySnapshot) => {
if (querySnapshot.empty) return // If there is no data just do nothing.
let promises = []
for (let doc of querySnapshot.docs) {
// Do all promisess in parallel
promises.push(animebyId(({ ...doc.data(), id: doc.id }.animeId)))
}
// Await all promises
const results = await Promise.all(promises)
if(!results.length) return // If no result abort.
let animes = []
for (let doc of results) {
animes.push(doc.media[0]);
}
setList(animes);
})
}
You need to know that onSnapshot()
is an observable function. It will trigger a function he has inside every time data he listens is changed. If you change route, you need to stop listening, or you end up having many opened observers. To do that, just invoke the function it returns. Like unsub()
And remember that after init that function you have empty querySnapshot
because of request is going to server, and it needs to back with data. Unless you're using browser storage "persistence", you may get first "old" data from cache. If you want to know more, read about firestore persistence.
Upvotes: 1
Reputation: 725
I would recommend using for-of loop instead of forEach it will wait until your loop is complete before calling setList, below is the code you can try
useEffect(() => {
const getId = async () => {
onSnapshot(q, async (querySnapshot) => {
let animes = [];
for (let doc of querySnapshot) {
const response = await animebyId(({ ...doc.data(), id: doc.id }.animeId));
animes.push(response.media[0]);
}
setList(animes);
})
}
getId()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
EDIT: can you give it try to below useEffect
useEffect(() => {
const getId = async () => {
onSnapshot(q, async (querySnapshot) => {
let animes = [];
querySnapshot.forEach(doc => {
const anime = { ...doc.data(), id: doc.id };
animes.push(anime);
})
const animesNew = [];
for (let anime of animes) {
const response = await animebyId(anime.animeId);
animesNew.push(response.media[0]);
}
setList(animesNew);
})
}
getId()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Upvotes: 1