React 学习站
React›基础›入门

条件渲染与列表渲染

入门基础

一句话定义

在 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)。

自测题

  1. {list.length && <Pager />} 当 list 为空时界面显示什么?三种修正写法各是什么?
  2. 用 ?: 重写下面嵌套 if/else,并说明什么情况下应改用提前 return:

``jsx if (a) return <A/>; else if (b) return <B/>; else return <C/>; ``

  1. data.filter(...).map(...) 的结果可以再 .sort() 吗?会不会违反不可变原则?(可以,前提是 filter 已产生新数组——sort 在新数组上原地排序是安全的。)
  2. 为什么 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>