如何在React中实现弹性网格布局展示男装上衣商品
React Bootstrap 4×3商品布局+分页实现方案
1. 定义基础数据与状态
先把硬编码的卡片逻辑替换为动态数据驱动,首先定义商品数据结构、分页状态、图片有效性校验逻辑:
- 每页固定展示12条(4列×3行),不足12条时补全空占位项保证布局稳定
- 封装图片URL有效性校验函数,支持预校验和加载失败兜底
import { useState, useMemo } from 'react'; import { Card, Row, Col, Pagination } from 'react-bootstrap'; // 工具函数:校验图片URL是否有效 const isValidImageUrl = (url) => { return url && typeof url === 'string' && /^(https?:\/\/.*\.(?:png|jpg|jpeg|webp|gif))/i.test(url); }; // 替换为你实际的商品数据源(接口返回/本地定义都可) const MOCK_PRODUCT_LIST = [ { id: 1, name: '休闲纯棉T恤', imgUrl: '你的有效图片地址', price: 99 }, // 更多商品数据... ]; const PAGE_SIZE = 12;
2. 实现分页逻辑
用useMemo计算当前页需要渲染的列表项,自动补全占位项到12条:
function ProductGrid() { const [currentPage, setCurrentPage] = useState(1); // 计算总页数 const totalPage = useMemo(() => { return Math.ceil(MOCK_PRODUCT_LIST.length / PAGE_SIZE); }, []); // 计算当前页展示的项,不足12条补空占位 const currentPageItems = useMemo(() => { const start = (currentPage - 1) * PAGE_SIZE; const end = start + PAGE_SIZE; const sliced = MOCK_PRODUCT_LIST.slice(start, end); // 补全到12个占位项,保证布局始终是3行4列不会错乱 return [...sliced, ...Array(PAGE_SIZE - sliced.length).fill(null)]; }, [currentPage]);
3. 实现4×3网格渲染
用Bootstrap栅格系统实现每行4列,有效URL渲染卡片,无效/空项留空:
return ( <div className="container my-4"> {/* 商品网格区域 */} <Row className="g-3"> {currentPageItems.map((item, index) => ( <Col key={index} xs={6} sm={4} md={3}> {/* 仅当商品存在且图片URL有效时渲染卡片 */} {item && isValidImageUrl(item.imgUrl) ? ( <Card> <Card.Img variant="top" src={item.imgUrl} alt={item.name} onError={(e) => e.target.parentElement.parentElement.style.display = 'none'} /> <Card.Body> <Card.Title>{item.name}</Card.Title> <Card.Text>¥{item.price}</Card.Text> </Card.Body> </Card> ) : ( // 空占位容器,高度和卡片保持一致,避免布局错位 <div className="w-100" style={{ height: '320px' }} /> )} </Col> ))} </Row> {/* 分页控件,总页数大于1时才显示 */} {totalPage > 1 && ( <Pagination className="mt-4 justify-content-center"> <Pagination.Prev disabled={currentPage === 1} onClick={() => setCurrentPage(prev => prev - 1)} /> {Array.from({ length: totalPage }, (_, i) => i + 1).map(page => ( <Pagination.Item key={page} active={page === currentPage} onClick={() => setCurrentPage(page)}> {page} </Pagination.Item> ))} <Pagination.Next disabled={currentPage === totalPage} onClick={() => setCurrentPage(prev => prev + 1)} /> </Pagination> )} </div> ); } export default ProductGrid;
补充优化点
- 上述代码已经加了图片加载失败的兜底逻辑,即使预校验漏判也不会出现裂图
- 可根据实际设计调整空占位容器的高度、栅格断点参数,适配不同设备的展示需求
- 如果是大数据量场景,把前端分页逻辑改成接口分页即可,仅需调整currentPage变化时拉取对应页数据的逻辑
内容的提问来源于stack exchange,提问作者JLoh17
相关产品推荐
相关产品推荐

