Search code examples
javascriptreactjsreact-context

React with conditional promises chain and wait for them all before send dispatch


I'm new to react. I have a user list that fetched once when the component mounts. and before the results dispatched to the reducer, it needs to loop inside the userlist and get user fullname/title from another endpoint, then add a new property to userlist object. I cant figure out how to wait for all promises (getUserById() function) to finish before calling a dispatch. I tried the solution here, but failed: How to return many Promises and wait for them all before doing other stuff

code below just to illustrate what I want:

  const {
    listUsers,
    fetchUserData
  } = useContext(GlobalContext);

  const getUserById = async (userId) => {
    return sp.web.siteUsers.getById(userId).get().then(user => user.Title);
  }

  useEffect(() => {
    sp.web.lists.getById("8C271450-D3F9-489C-B4FC-9C7470594466").items.get()
    .then(userLists => {
      userLists = userLists.map(list => {
        if (list.Person_x0020_ResponsibleId) {
          getUserById(list.Person_x0020_ResponsibleId).then(username => {
            list['Person_Responsible'] = username;  // -> fetch user fullname and title
          })
        } else {  // -> if id is null
          list['Person_Responsible'] = '-';
        }
        return list
      });

      fetchListSuccess(userLists);  // -> dispatch result to reducer
    });
  }, []);


Solution

  • You can accomplish this using Promise.all. First you need an array of promises from your second API calls. Then we'll give this array to Promise.all, and it will wait until they all resolve.

    I've rewritten using async/await syntax. It works the same as using .then with the promises, but when you're working with a promise chain that's this complex it's easier to follow with async/await.

    useEffect(async () => {
      const userLists = await sp.web.lists.getById('8C271450-D3F9-489C-B4FC-9C7470594466').items.get();
      const promises = userLists.map(async (list) => {
        if (list.Person_x0020_ResponsibleId) {
          const username = await getUserById(list.Person_x0020_ResponsibleId);
          list.Person_Responsible = username; // -> fetch user fullname and title
        } else { // -> if id is null
          list.Person_Responsible = '-';
        }
        return list;
      });
      await Promise.all(promises);
      fetchListSuccess(userLists); // -> dispatch result to reducer
    }, []);
    

    A few notes:

    • You don't really need to reassign userLists in the map, because you're just adding a property to existing objects. This will happen without the map.
    • Now the map is being used to return an array of promises for your second API calls. This is used by the Promise.all to wait until all those promises resolve.