0

I have a Firebase database as such (for instance):

{
    'itemCategories': [
        _categoryKey1: {
            name: 'Category 1'
        },
        _categoryKey2: {
            name: 'Category 2'
        },
        _categoryKey3: {
            name: 'Category 3'
        },
    ],
    'items': [
        _itemKey1: {
            categoryId: '_categoryKey1',
            name: 'Item 1',
        }
        _itemKey2: {
            categoryId: '_categoryKey2',
            name: 'Item 2',
        }
        _itemKey3: {
            categoryId: '_categoryKey1',
            name: 'Item 3',
        }
        _itemKey4: {
            categoryId: '_categoryKey3',
            name: 'Item 4',
        }
        _itemKey5: {
            categoryId: '_categoryKey3',
            name: 'Item 5',
        }
    ]
}

What I want to get is an array of items grouped by categories, as such:

itemList = [
    {
        category: 'Category 1',
        items: [
            {
                id: '_itemKey1',
                categoryId: '_categoryKey1',
                name: 'Item 1',
            },
            {
                id: '_itemKey3',
                categoryId: '_categoryKey1',
                name: 'Item 3',
            }
        ]
    },
    {
        category: 'Category 2',
        items: [
            {
                id: '_itemKey2',
                categoryId: '_categoryKey2',
                name: 'Item 2',
            }
        ]
    },
    {
        category: 'Category 3',
        items: [
            {
                id: '_itemKey4',
                categoryId: '_categoryKey3',
                name: 'Item 4',
            },
            {
                id: '_itemKey5',
                categoryId: '_categoryKey3',
                name: 'Item 5',
            },
        ]
    },
]

I use a react-redux action to achieve this, like so:

export const setItemList = (list) => {
    return {
        type: 'setItemList',
        value: list
    }
}

export const getItemListByCategory = () => {
    return (dispatch) => {

        let itemList = []

        firebase.database().ref('itemCategories').on('value', (snapCategory) => {
            snapCategory.forEach((category) => {
                const categoryId = category.key

                firebase.database().ref('items').orderByChild('categoryId').equalTo(categoryId).on('value', (snapItem) => {
                    let items = []
                    snapItem.forEach((item) => {
                        items.push({
                            id: item.key,
                            ...item.val()
                        })
                    })

                    itemList.push({
                        category: category.val().name,
                        items: items
                    })

                    // This is where I think is the issue
                    dispatch(setItemList(itemList))
                })
            })

            // The dispatch should be there, when every calls resolved
        })
    }
}

This works pretty well, for the most part. As I'm in a forEach loop, I will retrieve datas category by category, and actually dispatch multiple partial itemList until the forEach loop ends. Obviously, I'd prefer to wait until the loop ends then dispatch the complete itemList.

I can't figure out how to wait for the loop to end - and all the calls to resolve. I feel like I should work with Promise but I'm not sure how to achieve it.

pistou
  • 2,799
  • 5
  • 33
  • 60

1 Answers1

1

How about using Promise.all()? Instead of triggering dispatch in firebase.database().ref('items').orderByChild('categoryId').equalTo(categoryId).on You could add this call to a promises array. Something like this:

const promises = [];

snapCategory.foreach(snap => {

   promises.push(your calls here, instead of dispatch return the list here)
}) 

Promise.all(promises).then((...args) => {
// args should contain all lists here, you can manipulate them as you need
// and dispatch afterwards
})
Daniel Dimitrov
  • 1,848
  • 21
  • 35