react-loading-skeleton在组件中不显示问题求助
解决react-loading-skeleton加载时不显示的问题
问题根源分析
- isLoading状态提前终止:你在两个独立的异步请求中分别调用
setIsLoading(false),只要其中一个请求完成,加载状态就会被关闭,另一个请求还在进行时骨架屏已经消失。 - 初始无数据时无骨架占位:
shopCoupons初始为空数组,map不会渲染任何Coupon组件,所以即使isLoading为true,页面上也看不到骨架屏。 - Coupon组件部分区域未处理加载状态:图片和描述区域没有在加载时显示骨架屏,体验不完整。
具体修复方案
1. 同步两个异步请求的加载状态
使用Promise.all等待两个请求都完成后,再将isLoading设为false,确保所有数据加载完成后才关闭骨架屏。
2. 加载时渲染骨架占位
当isLoading为true时,渲染固定数量的骨架屏(模拟优惠券列表),而不是依赖shopCoupons的数据。
3. 完善Coupon组件的加载状态
给图片、描述区域也添加骨架屏,保证整个组件在加载时都有占位效果。
修改后的代码
ShopPage.jsx
import Skeleton from 'react-loading-skeleton' import 'react-loading-skeleton/dist/skeleton.css' import Coupon from "../../components/coupon/Coupon"; import { Box } from '@mui/material'; export default function ShopPage() { const [disable, setDisable] = useState(false); const [shop, setShop] = useState(""); const shopName = useParams().shopName; const [shopCoupons, setCoupons] = useState([]); const { search } = useLocation(); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const fetchData = async () => { try { // 同时发起两个请求,等待都完成 const [shopRes, couponsRes] = await Promise.all([ axios.get(`/shops/${shopName}`), axios.get(`/coupons/${shopName}`) ]); setShop(shopRes.data); setCoupons(couponsRes.data); } catch (err) { console.error('数据加载失败:', err); } finally { // 无论成功失败,都关闭加载状态 setIsLoading(false); } }; fetchData(); }, [shopName]); return ( <> <div className="ShopPage"> <div className="ShopPageContainer"> <div className="ShopPageInfoContainer"> <div className="ShopPageShopNameButton"> <div> <h5 className="ShopPageShopName"> {isLoading ? <Skeleton /> : `${shop.shopName} `} </h5> </div> </div> </div> <Box className="ShopPageCouponsContainer"> {isLoading ? ( // 加载时渲染3个骨架占位,数量可根据需求调整 Array.from({ length: 3 }).map((_, idx) => ( <div key={idx} className="coupon-container"> <Skeleton height={120} borderRadius={8} /> </div> )) ) : ( shopCoupons.map(c => ( <Coupon isLoading={isLoading} coupon={c} key={c.id}/> )) )} </Box> </div> </div> </> ) }
Coupon.jsx
import Skeleton from 'react-loading-skeleton' import 'react-loading-skeleton/dist/skeleton.css' export default function Coupon({ coupon, isLoading }) { return ( <div className="coupon-container"> <div className="coupon"> <div className="co-img"> {isLoading ? ( <Skeleton circle width={60} height={60} /> ) : ( coupon.photo && <img className="couponLogo" src={coupon.shopPic} alt={coupon.shopName} /> )} </div> <div className="content"> <a href={`/shops/${coupon.shopName}`} target="_self"> <h2 className="couponShopName"> {isLoading ? <Skeleton width={150} /> : coupon.shopName} </h2> </a> <h1>{isLoading ? <Skeleton width={200} /> : coupon.title}</h1> </div> <div className="couponDesc"> {isLoading ? <Skeleton count={2} /> : coupon.desc} </div> </div> </div> ); }
额外注意事项
key属性不要直接传递对象c,建议使用优惠券的唯一ID(比如c.id),避免React渲染警告。- 可以根据实际布局调整Skeleton的尺寸(
width、height、count等属性),让骨架屏更贴近真实组件的样式。 - 记得处理请求失败的情况,避免加载状态一直卡在
true。
内容的提问来源于stack exchange,提问作者sultan.h
相关产品推荐
相关产品推荐

