1

I'm building something like a Messenger using React + Redux and Real-Time Database from Firebase.

I'm used to retrieve data from an API like this:

export const fetchContacts = () => {
    return async dispatch => {
        dispatch(fetchContactsStart());

        try {
            const response = await axios.get('....');

            dispatch(fetchContactsSuccess(...));
        } catch(e) {
            dispatch(fetchContactsFail(...));
        }
    }
}

Now in firebase I have the following data: enter image description here

What I need is to get all user contacts and then get user info associated with those contacts and build a structure like: [{email: ..., username: ...}, {email: ..., username: ...}, ...]. After getting all the data in that format I want to dispatch the action to my reducer.

Right now I have the following code:

export const fetchContacts = () => {
    return dispatch => {
        dispatch(fetchContactsStart());

        const userEmail = firebase.auth().currentUser.email;
        let data = [];

        firebase.database().ref(`users_contacts/${Base64.encode(userEmail)}`)
            .on('value', contacts => contacts.forEach(contact => {
                firebase.database().ref(`users/${Base64.encode(contact.val().contact)}`)
                    .on('value', user => {
                        data.push({ email: contact.val().contact, username: user.val().username });
                        console.log(data)
                    })
            }
        ))
    }
}

This works but I don't know how to dispatch the action only when the data is fully formed, is there any solution for this? Thanks for the help!

1 Answer 1

1

When you're waiting for multiple asynchronous operations to complete, the solution is usually to use Promise.all. In this case, that'd look something like this:

export const fetchContacts = () => {
  return dispatch => {
    dispatch(fetchContactsStart());

    const userEmail = firebase.auth().currentUser.email;
    let data = [];

    firebase.database().ref(`users_contacts/${Base64.encode(userEmail)}`).once('value', contacts => {
      let promises = [];
      let map = {};
      contacts.forEach(contact => {
        const contactId = Base64.encode(contact.val().contact);
        map[contactId] = contact.val().contact;
        promises.push(firebase.database().ref(`users/${contactId}`).once('value'));
      });
      Promise.all(promises).then((users) => {
        users.forEach((user) =>
          data.push({ email: map[user.key], username: user.val().username });
        })
        // TODO: dispatch the result here
      }
    ))
  }
}

The main changes in here:

  • Now uses once() for loading the user data, so that is only loads once and returns a promise.
  • Uses Promise.all to wait until all profiles are loaded.
  • Added a map to look up the email address in the inner callback.
Sign up to request clarification or add additional context in comments.

1 Comment

I think I have to dispatch my action inside the then. Apart from that it works really the way i need it. Thank you so much for saving my day!

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.