Search code examples
reactjspromisefetchuse-effect

Empty Array is being returned before PromiseAll is resolved when used in useEffect method


I am iterating through a multidimensional array then pushing my data to a new array. When I log my array using the 'then' chain it logs empty. Not sure I am goin about this the proper way.

Array.js

export const Cars = [
    {
      type: "A",
      cars: [
        { name: "buick", id: "1259263" },
        { name: "ford", id: "1299830" },
        { name: "audi", id: "0181545" },
        { name: "gmc", id: "0016024" },
      ],
    },
    {
      type: "B",
      cars: [
        { name: "mazada", id: "1306193" },
        { name: "chevy", id: "1374540" },
        { name: "nissan", id: "1419526" },
        { name: "toyota", id: "1333007" },
      ],
    },
    {
      type: "C",
      cars: [
        { name: "bmw", id: "1259297" },
        { name: "porsche", id: "1305493" },
        { name: "tesla", id: "1042547" },
        { name: "mercedes", id: "1012982" },
      ],
    },
  ];

CarComponent.js

...
export const CarComponent = () => {
  const myArr = [];
  useEffect(() => {
    const fetchList = () => {
      Promise.all(
        Cars.map((car) => {
          return car.cars.map((id) => {
            return new Promise((resolve) => {
              fetch(`/api/=${id.id}`).then((response) => {
                return new Promise(() => {
                  response.json().then((id) => {
                    console.log(id); //<----returns normal
                    myArr.push(id);
                    resolve();
                  });
                });
              });
            });
          });
        }),
      ).then(() => {
        console.log("myArr", myArr); //<-----array is empty?
      })
    };
    fetchList();
  }, []);

...

Solution

  • Look at

      Promise.all(
        Cars.map((car) => {
          return car.cars.map((id) => {
    

    The item being returned from the mapper function is not a Promise, but an array - the return car.cars.map needs to be changed to a Promise.

    You should also avoid the explicit Promise construction antipattern.

    const fetchList = () => {
        Promise.all(
            Cars.map(({ cars }) => Promise.all(
                cars.map(({ id }) =>
                    fetch(`/api/=${id}`)
                        .then(res => res.json())
                        .then((result) => myArr.push(result))
                )
            ))
        ).then(() => {
            console.log("myArr", myArr);
        })
    };
    

    Another option, rather than pushing to an external array:

    const fetchList = () => {
        Promise.all(
            Cars.map(({ cars }) => Promise.all(
                cars.map(({ id }) =>
                    fetch(`/api/=${id}`)
                        .then(res => res.json())
                )
            ))
        ).then((results) => {
            console.log("myArr", results.flat());
        })
        .catch(handleErrors); // don't forget this part
    };