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>
</>
)
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 (
<>
...
</>
)
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.
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(......)}
...
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With