Как правильно добавлять данные из цикла fetch в массив. Нет возможности обратится к индексу и длине массива

[Такой массив получается в array и в итоге в ships, нет возможности обратится к индексу и длине массива1

const [ ships, setShips ] = useState()

useEffect(()=> {
    fetch("https://swapi.dev/api/films/2")
        .then(res => res.json())
        .then(film => {
            const starships = film.starships //Ссылки на данные конкретных объектов
            const array = []
            starships.forEach(ship => {
                fetch(ship)
                    .then(res => res.json())
                    .then(data => { 
                        array[array.length] = data
                    })
            })
            console.log(array)
            setShips(array) 
        })          
}, [])

Ответы (2 шт):

Автор решения: Sergei Kirjanov

В данном случае я бы поменял серверный API, чтоб можно было запрашивать подробности starships за раз. Т. к. иначе получается много лишних http запросов.

array[array.length] = data работает не очень, т. к. реакт не узнает, что стейт поменялся.

Если вызывать set* после каждого мелкого запроса, будет наоборот лишний рендеринг.

Можно решить на клиенте через Promise.all:

https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

    useEffect(()=> {
        fetch("https://swapi.dev/api/films/2").then(res => res.json())
            .then(film => {
                const starships = film.starships //Ссылки на данные конкретных объектов
                return Promise.all(starships.map(ship => fetch(ship).then(res => res.json())))
            }).then(data=>setShips(data))         
    }, [])
→ Ссылка
Автор решения: vsemozhebuty

Поскольку ваш пример кода не самодостаточный, мне трудно проверить решение, так что могу только предположить. Проблема в том, что starships.forEach() обычно не подходит для работы с асинхронным кодом: в вашем варианте он запустит все запросы синхронно и не будет дожидаться их окончания, поэтому дальнейший код будет иметь дело с пустым массивом. Возможно, самый простой способ — использовать цикл в async/await функции, если вам подходит, что запросы будут отсылаться последовательно. Если вам нужно параллельное выполнение запросов с ожиданием конечного результата, тогда стоит рассмотреть Promise.all(), как вам посоветовали в другом ответе.

fetch("https://swapi.dev/api/films/2")
  .then(res => res.json())
  .then(async (film) => {
    const starships = film.starships
    const array = []
    for (const ship of starships) {
      const res = await fetch(ship)
      const data = await res.json()
      array.push(data)
    }
    console.log(array)
    setShips(array)
  })
→ Ссылка