1
   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:

  1. I think my mistake is listening to a query to from a firestorm, then usestate on the result is make this code fail to map. look on useEffect
  2. animeById is an API file, using Axios, in another page is good and can render the map function. but in this page, I want to try using query snapshot.

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))
}
0

2 Answers 2

1

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
  }, []);
Sign up to request clarification or add additional context in comments.

5 Comments

i got console like this : Uncaught (in promise) TypeError: query snapshot is not iterable
what does animebyId function does?
i update the post for animebyid
Can you try the new useEffect code?
whoa, it works, thanks a lot! should we use a new array to setState? I'm confused in this part. why do we setState the first object? sorry ask to much
1

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.

2 Comments

@AlfianNahar 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()
thank you very much, I got it. I will read more about the firestore. this is my first project using firebase

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.