一句话定义
在 JSX 里做条件渲染就是写 JavaScript 表达式(&&、?:、提前 return);列表渲染就是 array.map() 把数据数组映射成 JSX 数组——没有模板指令,全部是语言本身。
为什么重要
这是把静态组件变成「真实界面」的最后一块语法拼图。同时它埋着两个新手必踩的坑:短路陷阱 0 && ... 与 map 忘传 key(后者在 kp-009 展开)。
前置知识
- JS 的
&&/||/??求值规则、三元表达式、Array.map。 - kp-002
{}里只能放表达式。
核心概念
- && 短路渲染:
cond && <X/>,cond 为假值时不渲染 X(但0会被打印出来!)。 - 三元渲染:二选一分支。
- 提前 return:复杂条件先守卫再渲染主 JSX,可读性最佳。
- 变量提取:把「要渲染什么」算成变量再放
{}。 - map + key:数组 → 组件数组;key 决定复用身份(kp-009)。
原理 / 机制
条件渲染的四种姿势,按推荐度排列:
jsxfunction Status({ loading, error, empty, items }) {
// 1. 提前 return:守卫式,推荐用于「分支互斥且较长」
if (loading) return <Spinner />;
if (error) return <p>出错了:{error.message}</p>;
if (empty) return <p>暂无数据</p>;
// 2. 变量提取:主结构保持清晰
const badge = items.length > 0 && <Badge count={items.length} />;
return (
<div>
{badge}
{/* 3. && 短路:单分支开关 */}
{items.length > 0 && <List items={items} />}
{/* 4. 三元:二选一 */}
<footer>{items.length > 10 ? <Pager /> : <span>一页搞定</span>}</footer>
</div>
);
}
短路陷阱的完整机制:&& 返回第一个假值本身,而不是布尔。
jsxconst count = 0;
{count && <Badge count={count} />}
// count 为 0 → 表达式值是数字 0 → React 渲染出 "0"
// 修正:{count > 0 && <Badge count={count} />}
// 修正:{(count ?? 0) > 0 && ...} 或 {!!count && ...}
列表渲染:
jsxconst rows = items.map(item => (
<Row key={item.id} item={item} />
));
return <ul>{rows}</ul>;
map 返回的新数组就是合法的 JSX 子节点——React 递归渲染数组中的每个元素。key 是给数组内的兄弟元素起名字,用途是协调时识别「谁是谁」(kp-009 详述)。
直观类比
条件渲染像舞台灯光矩阵:每个灯的亮灭由一条布尔公式决定(你可以写 if、写 ?:、写 &&),控制器(React)只按公式给灯,不关心你用了哪种写法。列表渲染像打印成绩单:模板只有一行「{name}: {score}」,打印机按数组逐行吐出;key 就是每行左边的学号——学号写错或重号,老师(React)改成绩时就会改错人。
实例 / 案例
一个组合了全部技巧的搜索列表:
jsxfunction SearchResults({ query }) {
const [data, setData] = useState(null);
if (data === null) return <p>加载中…</p>;
const filtered = data.filter(x =>
x.title.toLowerCase().includes(query.toLowerCase())
);
return (
<section>
<h2>结果({filtered.length})</h2>
{filtered.length === 0 ? (
<EmptyState query={query} />
) : (
<ul>
{filtered.map(x => (
<li key={x.id}>
{x.title}
{x.isNew && <Tag>NEW</Tag>}
</li>
))}
</ul>
)}
</section>
);
}
注意 filtered 是渲染期间从 data 派生的(kp-005 的「不要存派生值」),不需要第二个 state。
常见误区
0 && ...渲染出 "0":见上;凡左侧可能是 number,先用比较表达式包一层。- 忘 key / 用 index 当 key:警告与列表错乱(详见 kp-009:index key 在删除/插入时错位)。
.map()里写if:{items.map(x => { if (x.ok) return <A/>; return <B/>; })}合法但易漏 return;优先用三元。- 渲染空数组:
<ul>{[]}</ul>不报错,但请决定「空态 UI」该长什么样(EmptyState 是产品体验的一部分)。 - 把复杂布尔逻辑全部内联进 JSX:嵌套三层三元后无法阅读;先在组件体里算成语义化变量(
const canSubmit = name && agree && !submitting)。
自测题
{list.length && <Pager />}当 list 为空时界面显示什么?三种修正写法各是什么?- 用
?:重写下面嵌套if/else,并说明什么情况下应改用提前 return:
``jsx if (a) return <A/>; else if (b) return <B/>; else return <C/>; ``
data.filter(...).map(...)的结果可以再.sort()吗?会不会违反不可变原则?(可以,前提是 filter 已产生新数组——sort 在新数组上原地排序是安全的。)- 为什么 key 应该来自数据(如 id)而不是
key={i}?举一个删除场景的具体错乱。
与其他知识点的关系
- 向前:语法全部来自 kp-002 的「表达式」规则。
- 向后:kp-009 回答「key 到底怎么选」;kp-020 用 Suspense 替代手写
loading分支的范式升级;kp-019 提醒:超大列表的 map 应配合虚拟化,而不是先上记忆化。
延伸阅读
- Conditional Rendering:<https://react.dev/learn/conditional-rendering>
- Rendering Lists:<https://react.dev/learn/rendering-lists>