四、React列表渲染与条件渲染

📅 2026/8/22 18:03:31
四、React列表渲染与条件渲染
一、条件渲染条件渲染可以根据组件的状态、数据或权限决定页面渲染什么内容是 React 开发中的高频技巧。下面按从简单到高级的顺序梳理几种常用实现方式。1. 基础内联判断最常用这类方法直接写在 JSX 的{}大括号里适合处理简单的条件逻辑。三元运算符适用场景二选一非 A 即 B。实际例子用户登录了显示欢迎语没登录显示登录按钮。{isLoggedIn ? h1欢迎回来/h1 : button请登录/button}逻辑与()适用场景满足条件才显示不满足就不显示没有 else 分支。实际例子只有当购物车有商品时才渲染商品列表。{cartItems.length 0 ShoppingList items{cartItems} /}⚠️ 新手避坑千万别写成cartItems.length ...如果长度为 0页面上会直接显示一个数字 0写法适用场景是否有 else 分支典型示例三元运算符? :二选一非 A 即 B是{isLoggedIn ? h1欢迎回来/h1 : button请登录/button}逻辑与满足条件才显示不满足不显示否{cartItems.length 0 ShoppingList items{cartItems} /}2. 组件外置变量赋值最清晰当条件判断逻辑比较复杂或者嵌套太多时直接写在 JSX 里会让代码很难看。这时候我们可以把逻辑“提取”到 return 语句之前。if…else 变量适用场景多分支逻辑或者需要在条件判断前后打印日志console.log、做复杂计算。实际例子根据不同状态渲染不同的 UI。let content; if (status loading) { content Spinner /; } else if (status error) { content ErrorMsg /; } else { content DataList /; } return div{content}/div;switch…case 变量适用场景有多个固定的状态值需要匹配时。实际例子电商订单详情页根据订单状态待支付、待发货、已完成、已取消展示不同的操作按钮和提示。function OrderStatus({ status }) { // 1. 把复杂的判断逻辑提取成一个函数 const renderStatusUI () { switch (status) { case pending: return div classNametext-orange-500⏳ 待支付请在30分钟内完成付款/div; case shipped: return div classNametext-blue-500 商家已发货请耐心等待/div; case completed: return div classNametext-green-500✅ 订单已完成欢迎评价/div; default: return div classNametext-gray-500❓ 未知订单状态/div; } }; // 2. JSX 变得极其清爽 return ( div classNameorder-card h2订单详情/h2 {renderStatusUI()} /div ); }3. 高级渲染模式适合架构与复用当你的条件逻辑需要在多个组件中反复使用或者需要高度解耦时可以使用以下高级技巧。立即执行函数(IIFE)适用场景必须在 JSX 内部处理复杂的多条件分支如 switch不想污染组件外部变量。实际例子div {(() { switch (status) { case loading: return Spinner /; case success: return DataList /; default: return null; } })()} /div高阶组件(HOC)适用场景封装通用的条件逻辑比如权限控制。实际例子写一个withAdminAccess函数只有管理员才能看到被包裹的组件否则显示“无权限”。// 1. 定义一个权限包装盒 function withAdminAccess(WrappedComponent) { return function(props) { const isAdmin props.userRole admin; // 假设从 props 获取角色 if (!isAdmin) { return div style{{color:red}} 403 无权限仅管理员可访问此页面/div; } // 权限通过正常渲染原组件并把剩下的 props 传下去 return WrappedComponent {...props} /; }; } // 2. 普通的业务组件它根本不需要关心权限问题 function UserManagePanel() { return div这里是用户管理列表包含删除按钮.../div; } // 3. 把组件放进盒子里导出 export default withAdminAccess(UserManagePanel);Render Props(渲染属性)适用场景将“如何渲染”的决定权交给父组件实现细粒度的逻辑共享。实际例子获取当前鼠标位置或者获取用户在线状态。逻辑很复杂但展示方式千变万化。// 1. 状态提供者组件 function MouseTracker({ render }) { const [position, setPosition] React.useState({ x: 0, y: 0 }); React.useEffect(() { const handleMove (e) setPosition({ x: e.clientX, y: e.clientY }); window.addEventListener(mousemove, handleMove); return () window.removeEventListener(mousemove, handleMove); }, []); // 核心把状态作为参数传给 render 函数 return render(position); } // 2. 使用者决定怎么画 function App() { return ( MouseTracker render{(pos) ( div p当前鼠标坐标X{pos.x}, Y{pos.y}/p {/* 这里你可以随便画甚至画个跟随鼠标的图片 */} /div )} / ); }自定义 Hook适用场景将条件判断的逻辑如媒体查询、权限判断抽离成可复用的状态逻辑。实际例子判断当前设备是不是手机端媒体查询或者封装一个通用的“数据请求”逻辑。// 1. 封装一个判断屏幕宽度的 Hook function useIsMobile() { const [isMobile, setIsMobile] React.useState(window.innerWidth 768); React.useEffect(() { const handleResize () setIsMobile(window.innerWidth 768); window.addEventListener(resize, handleResize); return () window.removeEventListener(resize, handleResize); }, []); return isMobile; // 只返回一个布尔值 } // 2. 组件里像用普通变量一样使用它 function NavigationBar() { const isMobile useIsMobile(); return ( nav {isMobile ? ( button☰ 菜单/button ) : ( ul li首页/li li关于/li /ul )} /nav ); }模式适用场景特点立即执行函数(IIFE)JSX 内部处理复杂多条件分支逻辑内聚不污染组件外部变量高阶组件(HOC)封装通用条件逻辑如权限控制包裹组件复用逻辑Render Props父组件决定“如何渲染”细粒度共享渲染逻辑自定义 Hook复用条件相关状态逻辑组件中使用更简洁二、列表渲染列表渲染用于把数组数据批量展示为界面元素如商品卡片、新闻列表、聊天消息等。下面从最常用的map()开始再到 Fragment、循环/归并等进阶写法最后说明key的使用要点。1. 标准推荐使用 map() 方法这是 React 中最常见、最标准的列表渲染方式。无论是商品列表还是新闻列表99% 的场景都会用到它。实际例子渲染一个包含标题和 ID 的列表。const data [ { id: 1, title: React基础 }, { id: 2, title: JSX语法 } ]; return ( ul {data.map(item ( li key{item.id}{item.title}/li ))} /ul );当列表项的 JSX 结构比较复杂时建议将其封装成独立的子组件如ListItem /这样主组件的代码会清爽很多。map()本身会返回一个新数组通常直接放在 JSX 中交给 React 渲染如果需要复用或二次处理也可以先存入变量再渲染。2. 进阶写法使用 Fragment避免多余 DOM在渲染列表时有时我们需要为每一项渲染多个平级的标签比如标题和描述。如果强行用一个div包裹会导致 DOM 树中多出很多无意义的节点。这时可以使用React.Fragment简写为。实际例子return ( {data.map(item ( // 注意Fragment 必须使用完整写法并加上 key React.Fragment key{item.id} h3{item.title}/h3 p{item.description}/p /React.Fragment ))} / );补充说明简写形式.../无法携带key所以列表项需要key时必须使用React.Fragment key{...}的完整写法。3. 特殊场景使用 for 循环或 reduce()for 循环在渲染前通过 for 循环将 JSX 元素 push 到一个数组中然后再渲染该数组。这种方式可行但不符合 React 声明式的代码风格不推荐日常使用。当你需要在渲染列表之前进行复杂的逻辑控制比如break提前终止、continue跳过某些项或者需要同时遍历多个数组时map()会显得力不从心这时 for 循环就派上用场了。实际开发场景假设你要渲染一个“最新 5 条消息”的列表但后端返回了 100 条数据且你只想渲染前 5 条。用map需要配合slice而用 for 循环可以直接break。function MessageList({ messages }) { const items []; // 1. 准备一个空数组 // 2. 使用 for...of 循环比传统 for 更现代、易读 for (const msg of messages) { // 假设遇到已删除的消息就跳过 if (msg.isDeleted) continue; items.push( li key{msg.id} classNamemessage-item {msg.content} /li ); // 核心优势只要凑够了 5 条立刻停止循环节省性能 if (items.length 5) break; } // 3. 在 JSX 中直接渲染这个数组 return ul{items}/ul; }reduce() 方法当你需要在遍历数组的同时进行累加逻辑或复杂的数据转换时可以使用reduce()来构造 JSX 列表。当你不仅需要遍历数组生成列表还需要在遍历的过程中“记住”前一个元素的状态或者需要对数据进行分组、过滤并转换时reduce()是神器。它可以把“数据清洗”和“生成 JSX”合并在一步完成。实际开发场景假设你有一个包含日期的任务列表你需要按日期进行分组渲染例如今天、昨天、更早。reduce()可以在遍历过程中动态构建出分组后的 JSX 结构。function TaskList({ tasks }) { // 使用 reduce 将平铺的任务列表转换为按日期分组的 JSX 结构 const groupedTasks tasks.reduce((acc, task) { // 获取当前任务的日期标签 const dateLabel task.dateLabel; // 比如 今天, 昨天 // 检查累加器中是否已经有这个日期的分组 const existingGroup acc.find(group group.label dateLabel); if (existingGroup) { // 如果已有该分组直接把任务追加进去 existingGroup.children.push( li key{task.id}{task.title}/li ); } else { // 如果没有创建一个新的分组并加入累加器 acc.push({ label: dateLabel, children: [li key{task.id}{task.title}/li] }); } return acc; // 必须返回累加器 }, []); // 初始值是一个空数组 // 渲染分组后的结果 return ( div {groupedTasks.map(group ( div key{group.label} classNametask-group h3{group.label}/h3 ul{group.children}/ul /div ))} /div ); }方式适用场景注意事项for循环需要break/continue或同时遍历多个数组代码偏命令式不优先推荐reduce()遍历时分组、过滤、转换、累加必须显式返回累加结果4. 结合条件渲染与嵌套列表在实际业务中列表往往不是孤立存在的。条件渲染结合在渲染列表前先判断数组是否有数据。如果没有数据显示“暂无内容”的占位图。{items.length 0 ? ( ul{items.map(item li key{item.id}{item.name}/li)}/ul ) : ( p暂无数据/p )}嵌套列表当列表项中又包含子列表时可以在外层map()内部再调用一次map()并分别为内层元素设置key。{categories.map(category ( div key{category.id} h3{category.name}/h3 ul {category.children.map(child ( li key{child.id}{child.name}/li ))} /ul /div ))}5. 避坑指南在使用列表渲染时key属性是重中之重。React 依靠key来识别哪些元素改变了、添加了或删除了从而优化渲染性能。优先使用唯一 ID后端返回的数据通常都有 id这是最优选择。绝对禁止使用Math.random()作为 key这会导致每次重新渲染时 key 都不同React 会销毁所有旧组件并重新创建不仅导致页面卡顿还会丢失用户的输入状态。谨慎使用索引index作为 key只有在列表是纯静态不会重新排序、不会插入或删除元素的简单数组如纯字符串数组时才能用index。一旦列表顺序发生变化使用index会导致状态错乱等令人困惑的 Bug。