Search code examples
javascriptreactjsasync-awaitcomponentsreact-props

How to await data coming from an API as a prop in a component?


I am trying to send a prop on a component once my data loads from an API. However, even though I am using async await to await from my data, I am still getting an error of: Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

My process:

  • Have two set states: 1) updateLoading and 2) setRecordGames
  • Once the data is loaded, I will set updateLoading to false and then set recordGames with the data from the API.

My Problem: It seems that when I pass data in the component, React does not wait for my data load and gives me an error.

This is my code:


import useLoadRecords from '../../hooks/useLoadRecords'
import { CustomPieChart } from '../charts/CustomPieChart'

export const GameDistribution = () => {
const { records, loading } = useLoadRecords()
  let data = records

  console.log(data) // This logs out the records array

  return (
    <div>
      // once loading is false, render these components
      {!loading ? (
        <>
          <div>
            {recordGames.length > 0 ? (
              // This line seem to run as soon as the page loads and I think this is the issue. recordGames is empty at first, but will populate itself when the data loads
              records.length > 0 && <CustomPieChart data={data} />
            ) : (
              <div>
              No games
              </div>
            )}
          </div>
        </>
      ) : (
       <span>Loading...</span>
      )}
    </div>
  )
}

// useLoadRecords.js

import { useState, useEffect } from 'react'
import { API } from 'aws-amplify'
import { listRecordGames } from '../graphql/queries'

// Centralizes modal control
const useLoadRecords = () => {
  const [loading, setLoading] = useState(false)
  const [records, updateRecords] = useState([])

  useEffect(() => {
    fetchGames()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const fetchGames = async () => {
    try {
      let recordData = await API.graphql({
        query: listRecordGames,
      })

      setLoading(false)

      let records = recordData.data.listRecordGames.items.map(
        (item) => item.name
      )
      let result = Object.values(
        records.reduce((acc, el) => {
          if (!acc[el]) acc[el] = { name: el, plays: 0 }
          acc[el].plays++
          return acc
        }, {})
      )
      updateRecords(result)
    } catch (err) {
      console.error(err)
    }
  }

  return { records, loading }
}

export default useLoadRecords


Solution

  • I would make a hook for the data and setData to it when fetching , you can clone the data using spread operator and this pass it. or better return that component only if there is something in data for example

    {
    data &&  <CustomPieChart data={recordGames} />
    }
    

    and it would be nice to make a some loader (gif/svg) using your loading hook so it can be shown when the data is still being fetched.