Search code examples
reactjsfirebasereduxreact-context

Can an action be dispatched inside the THEN construct of a promise?


The following is a component that maps through the objects in a context variable and renders them.

const MyGroups = () => {
    const { myGroups } = useContext(GlobalContext);

    return (
        <div className="my__groups">
            <h1 className="my__groups__heading">My Groups</h1>
            <div className="my__groups__underline"></div>
            <div className="my__groups__grid__container">
                {
                    myGroups.map(({id, data}) => (
                        <GroupCard
                            key={id}
                            name={data.name}
                            image={data.image} 
                        />
                    ))
                }
            </div>
        </div>
    )
}

The following is my store function which I use to fetch my data from Firebase and dispatch an action to the Reducer.

function fetchGroupsFromDatabase(id) {
        let myGroups = [];
        db.collection("users").doc(id).get() // Fetch user details with given id 
            .then(doc => {
                doc.data().groupIDs.map(groupID => { // Fetch all group IDs of the user
                    db.collection("groups").doc(groupID).get() // Fetch all the groups 
                        .then(doc => {
                            myGroups.push({id: doc.id, data: doc.data()})
                        })
                })
            })
            .then(() => {
                const action = {
                    type: FETCH_GROUPS_FROM_DATABASE,
                    payload: myGroups
                };
                dispatch(action);
            })
    }

Now, the problem is that the "GroupCards" that I want to render, are not rendering although I can see in the console that the context variable get's populated after some time.

Working perfectly with setTimeout()

However, I have observed that instead of dispatching the action within the THEN construct, if I dispatch my action after some seconds by setTimeout, my component renders perfectly, like this:

function fetchGroupsFromDatabase(id) {
        let myGroups = [];
        db.collection("users").doc(id).get() // Fetch user details with given id 
            .then(doc => {
                doc.data().groupIDs.map(groupID => { // Fetch all group IDs of the user
                    db.collection("groups").doc(groupID).get() // Fetch all the groups 
                        .then(doc => {
                            myGroups.push({id: doc.id, data: doc.data()})
                        })
                })
            })

            setTimeout(() => {
                const action = {
                    type: FETCH_GROUPS_FROM_DATABASE,
                    payload: myGroups
                };
                dispatch(action);
            }, 3000);
    }

I humbly request you to kindly spare some of your valuable time and provide some solution to my problem.

Thank You very much.


Solution

  • In order to wait for all of the groups .get() calls to resolve, and to dispatch with the results, use Promise.all:

    async function fetchGroupsFromDatabase(id) {
        const doc = await db.collection("users").doc(id).get() // Fetch user details with given id 
        const myGroups = await Promise.all(
            doc.data().groupIDs.map(groupID => // Fetch all group IDs of the user
                db.collection("groups").doc(groupID).get() // Fetch all the groups 
                    .then(doc => ({ id: doc.id, data: doc.data() }))
            )
        );
        const action = {
            type: FETCH_GROUPS_FROM_DATABASE,
            payload: myGroups
        };
        dispatch(action);
    }