如何延迟页面呈现直到从 api 收到数据
How to delay page rendering until data received from api
第一次使用 API 请求加载页面时出错。但是在页面加载后,如果我放回相同的代码,它就可以正常工作。有人可以帮助我在这里缺少什么吗?或者告诉我延迟页面加载直到数据从 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>
</>
)
问题
您的 productData
最初是 null
并将出现在任何后续渲染中,直到被 GET 更新要求。尝试访问 productData.variants
会引发错误,因为 productData
为空。
解决方案
您可以使用一些加载状态并有条件地呈现您的 UI。在 productData
状态上使用 null-check/optional 链接运算符。
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(......)}
...
);
它为空,因为它在您的 useState 挂钩中被初始化为空。
这是正常的。
useEffect 挂钩应如下所示。
useEffect(() => {
function getProductdata() {
const secret = "SECRET"
return fetch(`https://app.myapi.com/api/products/${data.productsCsv.id}`, {
headers: {
'Authorization': `Basic ${btoa(secret)}`,
'Accept': 'application/json'
}
});
}
getProductdata().then((request => request.json()))
.then(data => setProductData(data))
.catch(err=>console.log(err));
}, []);
您可以通过在模板中使用逻辑 AND && 运算符来检查变量是否不为空来防止显示数据。
{productData && productData.variants.map((variant,index)=> ...
我没有测试这段代码。
旁注:
That SECRET 不是秘密。它会出现在代码中。
设置一些状态和 return 另一个组件,直到您获得数据,它应该看起来像这样:
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 (
<>
...
</>
)
您收到此错误是因为 productData.variants
不存在,因此地图函数 returns 出错。
在 map 函数前添加检查 productData
的条件语句。
{productData ? (
productData.variants.map((variant,index)=>{
//rest of code
}
) : null}
因此,如果 productData
为 null
,则地图函数不会执行。这是一篇Ternary Operator,在写ReactJS时很有用
您甚至可以添加 <p>Loading Data</p>
而不仅仅是 null
这样用户就知道正在加载数据而不是空白区域:
{productData ? (
productData.variants.map((variant,index)=>{
//rest of code
}
) : (
<p>Loading Data...</p>
)}
第一次使用 API 请求加载页面时出错。但是在页面加载后,如果我放回相同的代码,它就可以正常工作。有人可以帮助我在这里缺少什么吗?或者告诉我延迟页面加载直到数据从 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>
</>
)
问题
您的 productData
最初是 null
并将出现在任何后续渲染中,直到被 GET 更新要求。尝试访问 productData.variants
会引发错误,因为 productData
为空。
解决方案
您可以使用一些加载状态并有条件地呈现您的 UI。在 productData
状态上使用 null-check/optional 链接运算符。
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(......)}
...
);
它为空,因为它在您的 useState 挂钩中被初始化为空。 这是正常的。
useEffect 挂钩应如下所示。
useEffect(() => {
function getProductdata() {
const secret = "SECRET"
return fetch(`https://app.myapi.com/api/products/${data.productsCsv.id}`, {
headers: {
'Authorization': `Basic ${btoa(secret)}`,
'Accept': 'application/json'
}
});
}
getProductdata().then((request => request.json()))
.then(data => setProductData(data))
.catch(err=>console.log(err));
}, []);
您可以通过在模板中使用逻辑 AND && 运算符来检查变量是否不为空来防止显示数据。
{productData && productData.variants.map((variant,index)=> ...
我没有测试这段代码。
旁注: That SECRET 不是秘密。它会出现在代码中。
设置一些状态和 return 另一个组件,直到您获得数据,它应该看起来像这样:
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 (
<>
...
</>
)
您收到此错误是因为 productData.variants
不存在,因此地图函数 returns 出错。
在 map 函数前添加检查 productData
的条件语句。
{productData ? (
productData.variants.map((variant,index)=>{
//rest of code
}
) : null}
因此,如果 productData
为 null
,则地图函数不会执行。这是一篇Ternary Operator,在写ReactJS时很有用
您甚至可以添加 <p>Loading Data</p>
而不仅仅是 null
这样用户就知道正在加载数据而不是空白区域:
{productData ? (
productData.variants.map((variant,index)=>{
//rest of code
}
) : (
<p>Loading Data...</p>
)}