Search code examples
javascriptreactjsreact-hookses6-promise

Aggregate results from multiple Promises in React Hook


I am trying to get a combined result from multiple promises inside a React hook. But when I use the hook, the function getAll immediately returns empty, instead of returning all MyTypes.

My Hook: (api.get returns a Promise<MyType[]>)

function useMyHook() {
  const api = useApiService();

  return {
    getAll,
  };

  function getAll(arr: number[]): MyType[] {
    const results: MyType[] = [];
    for (const u of arr) {
        api.get(u).then((res) => {
          results.push(...res);
        });
    }
    return [...new Set(results)];
  }

}

Usage:

function MyComponent() {
 // ...
 const myHook= useMyHook();
 
 const use = () => {
    // ...
    const numbers = [1, 2, 3];
    const myTypes = myHook.getAll(numbers);
    const count = myTypes.length; // this will always be 0
    // ...
  };
}

How can I make this work? I have tried multiple versions with promise chaining and async/await, but to no avail.


Solution

  • Since api.get returns a promise, getAll will need to return a promise too. It can't return a MyType[], since it takes time to assemble that array. I would use Promise.all to create a new promise that will wait for the individual promises, and then have some code after to combine the results.

    With async/await:

    function async getAll(arr: number[]): Promise<MyType[]> {
      const promises: Promise<MyType[]>[] = [];
      for (const u of arr) {
        promises.push(api.get(u));
      }
      const results = await Promise.all(promises);
      // results is an array of arrays, so we need to flatten it
      return [...new Set(results.flat())];
    }
    
    // used like: 
    const use = async () => {
      // ...
      const numbers = [1, 2, 3];
      const myTypes = await myHook.getAll(numbers);
      const count = myTypes.length;
      // ...
    };
    

    Or if you prefer using .then:

    function getAll(arr: number[]): Promise<MyType[]> {
      const promises: Promise<MyType[]>[] = [];
      for (const u of arr) {
        promises.push(api.get(u));
      }
      return Promise.all(promises).then(results => {
        // results is an array of arrays, so we need to flatten it
        return [...new Set(results.flat())];
      });
    }
    
    // used like: 
    const use = () => {
      // ...
      const numbers = [1, 2, 3];
      myHook.getAll(numbers).then(myTypes => {
        const count = myTypes.length;
        // ...
      });
    };