React如何延迟页面渲染直到API数据返回,解决首次加载报错问题
问题原因
- 你的初始化状态
productData的值为null,React组件首次渲染会先执行return里的渲染逻辑,此时直接调用productData.variants.map等价于读取null.variants,会直接抛出类型错误。 - useEffect的执行时机是组件首次渲染完成之后,所以API请求是在第一次渲染报错之后才发起的,等请求成功更新状态触发二次渲染时,才有值可以正常渲染,这也是你改代码触发热重载后功能正常的原因。
解决方案
你可以通过加空值判断/加载占位符的方式解决,不需要强行延迟页面渲染流程,体验更好:
方案1:添加加载态判断(推荐)
在数据返回前渲染加载提示/骨架屏,等数据加载完成再渲染实际内容,同时修正了你原代码中async/await和then混用、缺少尺寸状态定义等问题,修正后的完整代码如下:
import React, { useState, useEffect } from 'react' export default function ProductPage({ data }) { const [productData, setProductData] = useState(null) // 补充你代码里缺失的尺寸选择相关状态 const [activeSize, setActiveSize] = useState(null) useEffect(() => { getProductdata() }, []) async function getProductdata(){ try { const secret = "SECRET" const response = await fetch(`https://app.myapi.com/api/products/${data.productsCsv.id}`, { headers: { 'Authorization': `Basic ${btoa(secret)}`, 'Accept': 'application/json' } }) const result = await response.json() setProductData(result) } catch (err) { console.log(err) } } const toggleSize = (index) => { setActiveSize(index) } // 加载态判断,数据未返回时直接渲染加载提示 if (!productData) return <div>加载中...</div> return ( <> <div className="stock mb-4 "> <p className="tracking-wider mb-2">Size</p> {productData.variants.map((variant,index)=>{ 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' : ''}`} role="button" tabIndex={0} onClick={() => toggleSize(index)} onKeyDown={() => toggleSize(index)} 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': ''}`} disabled role="button" tabIndex={0} onClick={() => toggleSize(index)} onKeyDown={() => toggleSize(index)} key={index}>{variant.variation[0].option}-{variant.stock}</button> ) } })} </div> </> ) }
方案2:可选链兼容
如果不需要显示加载态,也可以用可选链运算符避免空值报错,写法如下:
{productData?.variants?.map((variant,index)=>{ // 原有渲染逻辑 })}
这种方式不会抛出错误,但数据加载完成前对应区域是空的,用户体验不如加载态友好。
内容的提问来源于stack exchange,提问作者vsoni
相关产品推荐
相关产品推荐

