【问题标题】:How to delay page rendering until data received from api如何延迟页面渲染,直到从 api 接收到数据
【发布时间】:2021-08-30 22:08:16
【问题描述】:

当第一次使用 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>
</>
)
                

【问题讨论】:

  • 通过`.catch(getProductdata)`重试请求
  • 但是您的productData 最初是null,并且将在任何后续渲染中出现,直到被 GET 请求更新。您还将控制台日志记录为无意的副作用,因此您看到实际记录的内容不应该是对任何事情的真正衡量标准。你期望发生什么?
  • 所以我的代码在“productData.variants”循环中出错,说无法读取 null 的属性。因此,如果我从 return 语句中删除代码并刷新我的页面,错误就消失了,当我在 return 语句中添加相同的代码时,它工作正常,因为 productData 不再为 null

标签: javascript reactjs api fetch


【解决方案1】:

它是空的,因为它在你的 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)=> ...

我没有测试这段代码。


旁注: 这个秘密不是秘密。它会出现在代码中。

【讨论】:

    【解决方案2】:

    问题

    您的productData 最初是null,并且将在任何后续渲染中出现,直到被 GET 请求更新。尝试访问productData.variants 会引发错误,因为productData 为空。

    解决方案

    您可以使用一些加载状态并有条件地呈现您的 UI。在productData 状态上使用空检查/可选链接运算符。

    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(......)}
      ...
    );
    

    【讨论】:

      【解决方案3】:

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

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

      所以如果productDatanull,则映射函数不会执行。这是一个Ternary Operator,在编写 ReactJS 时非常有用。

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

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

      【讨论】:

        【解决方案4】:

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

        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 (
          <>
          ...
          </>
        )
        

        【讨论】:

          猜你喜欢
          • 2021-06-06
          • 1970-01-01
          • 2016-11-22
          • 1970-01-01
          • 2014-06-02
          • 1970-01-01
          • 2022-10-12
          • 2018-06-13
          • 2018-04-05
          相关资源
          最近更新 更多