繁体   English   中英

如何延迟页面渲染,直到从 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>
</>
)
                

设置一点 state 并返回另一个组件,直到你有你的数据,它应该看起来像这样:

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最初null并将在任何后续渲染中出现,直到被 GET 请求更新。 尝试访问productData.variants会引发错误,因为productData是 null。

解决方案

您可以使用一些加载 state 并有条件地渲染您的 UI。 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(......)}
  ...
);

它是 null 因为它在你的 useState 挂钩中被初始化为 null。 这个是正常的。

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 && 运算符来检查变量是否不是 null 来阻止显示数据。

{productData && productData.variants.map((variant,index)=> ...

我没有测试这段代码。


旁注:秘密不是秘密。 它将出现在代码中。

您收到此错误是因为productData.variants不存在,因此 map function 返回错误。 在 map function 之前添加检查productData的条件语句。

{productData ? (
    productData.variants.map((variant,index)=>{
        //rest of code
    }
) : null}

因此,如果productDatanull ,则 map function 不会执行。 这是一个三元运算符,在编写 ReactJS 时非常有用。

您甚至可以添加<p>Loading Data</p>而不仅仅是null ,以便用户知道正在加载数据而不是空白区域:

{productData ? (
    productData.variants.map((variant,index)=>{
        //rest of code
    }
) : (
    <p>Loading Data...</p>
)}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM