Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delay page rendering until data received from API

When the page load for the first time with API request it errors out. but after page load if I put the same code back it works fine. Can someone please help what am I missing here. Or show me the trick to delay the page loading until data loads from API.

import React, { useState, useEffect } from 'react'

export default function ProductPage({ data }) {

const [productData, setProductData] = useState(null)

useEffect(() => {
    getProductdata()
}, [])

async function getProductdata(){
    const secret = "SECRET"
    const request = await fetch(`https://app.myapi.com/api/products/${data.productsCsv.id}`, {
        headers: {
            'Authorization': `Basic ${btoa(secret)}`,
            'Accept': 'application/json'
        }
    }).then((request => request.json()))
      .then(data => setProductData(data))
      .catch(err=>console.log(err))  
    }
    
   console.log("pdata",productData) // returns null on initial load and then it filled with data.

   
return (
    <>
     <div className="stock mb-4 ">
                    <p className="tracking-wider mb-2">Size</p>
                        {productData.variants.map((variant,index)=>{
                            <p>{variant.stock}</p>
                            if(variant.stock != 0){
                            return (
                                
                                
                                    <button className={`p-2 border-gray-200 border mr-2 mb-2 hover:bg-black hover:text-white cursor-pointer focus:border-black ${activeSize === index ? 'bg-black text-white' : null}`} role="button" tabIndex={0} 
                                    onClick={() => {toggleSize(index); setSize(size)}}
                                    onKeyDown={() => {toggleSize(index); setSize(size)}} key={index}>{variant.variation[0].option}-{variant.stock}</button>
                            
                                    
                                )
                            }
                            else {
                                return(
                                    <button className={`p-2 border-gray-200 border mr-2 mb-2 ${variant.stock == 0 ?'bg-gray-400 line-through text-red-500': null}`} disabled role="button" tabIndex={0} 
                                    onClick={() => {toggleSize(index); setSize(size)}}
                                    onKeyDown={() => {toggleSize(index); setSize(size)}} key={index}>{variant.variation[0].option}-{variant.stock}</button>
                                )
                            }
                            })} 
                            
                </div>
</>
)
                
like image 236
vsoni Avatar asked Oct 27 '25 05:10

vsoni


2 Answers

Set a bit of state and return another component until you have your data, it should look something like this:

import React, { useState, useEffect } from 'react'

export default function ProductPage({ data }) {

const [productData, setProductData] = useState(null)
const [loading, setLoading] = useSate(true) // set some state for loading

useEffect(() => {
    getProductdata()
}, [])

async function getProductdata(){
  const secret = "SECRET"
  const request = await fetch(`https://app.myapi.com/api/products/${data.productsCsv.id}`, {
    headers: {
      'Authorization': `Basic ${btoa(secret)}`,
      'Accept': 'application/json'
    }
    }).then((request => request.json()))
      .then((data) => {
        setProductData(data)
        setLoading(false) // set Loading to false when you have the data
      })
      .catch(err=>console.log(err))  
}
    
//use the piece of loading state to return other component until you have the data
if (loading) { 
  return (<div>Replace me with a loading component...</div>)
}
  
return (
  <>
  ...
  </>
)
like image 86
Mike Abeln Avatar answered Oct 29 '25 18:10

Mike Abeln


Issue

Your productData is initially null and will be on any subsequent renders until updated by the GET request. Attempting to access the productData.variants throws the error because productData is null.

Solution

You can use some loading state and conditionally render your UI. Use a null-check/optional chaining operator on the productData state.

const [productData, setProductData] = useState(null);
const [isLoading, setIsLoading] = useState(true); // <-- loading state

useEffect(() => {
  getProductdata();
}, []);

async function getProductdata() {
  setIsLoading(true); // <-- ensure loading true
  const secret = "SECRET";
  const request = await fetch(
    `https://app.myapi.com/api/products/${data.productsCsv.id}`,
    {
      headers: {
        'Authorization': `Basic ${btoa(secret)}`,
        'Accept': 'application/json'
      }
    }
  ).then((request => request.json()))
    .then(data => setProductData(data))
    .catch(err => console.log(err))
    .finally(() => setIsLoading(false); // <-- clear loading state success or fail
}

if (isLoading) return <div>Loading Data</div>; // <-- render loading UI

return (
  ...
  {productData?.variants?.map(......)}
  ...
);
like image 43
Drew Reese Avatar answered Oct 29 '25 19:10

Drew Reese