R05第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

条件渲染与状态分支

条件渲染与状态分支 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

真实的数据视图永远不止「成功」一种样子:加载中、加载失败、结果为空都要有界面。这一课帮你把四种分支补完整,并学会用单一状态字段取代互相打架的布尔值。读完你能独立为一个请求视图设计完整的状态分支。

问题场景

课程列表页第一版用了两个布尔值:

const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);

function load() {
  setLoading(true);
  fetchCourses().then(
    (list) => { setLoading(false); },
    () => { setLoading(false); setError(true); }
  );
}

测试时「重试按钮点了没反应」:重试时设了 loading 但忘了把 error 改回去,于是 error 和 loading 同时为 true——页面上「加载中」和「失败重试」两块同时出现。两个布尔值能组合出四种状态,其中至少一半是业务上不存在或互相矛盾的。

核心解释

分支问题本质是状态空间设计问题。 先把业务上合法的状态数一数:请求视图只有四种——loading(请求中)、error(失败)、empty(成功但没数据)、success(成功有数据)。任何时刻只能处于其中一种,这是一个互斥的集合,正适合用一个字符串字段表达:

const [status, setStatus] = useState("loading");

function load() {
  setStatus("loading");
  fetchCourses().then(
    (list) => setStatus(list.length > 0 ? "success" : "empty"),
    () => setStatus("error")
  );
}

四个值互斥,不可能再出现「同时加载和失败」。渲染分支按状态展开,用提前 return 让每个分支独立成块:

function CourseList({ status, courses, onRetry }) {
  if (status === "loading") return <p>加载中…</p>;
  if (status === "error") return <button onClick={onRetry}>失败,点我重试</button>;
  if (status === "empty") return <p>还没有课程</p>;
  return <ul>{courses.map((c) => <li key={c.id}>{c.title}</li>)}</ul>;
}

什么时候仍用布尔值?当状态确实只有两态且互不交叉时,比如「弹窗开/关」,单个布尔完全够用。判断标准还是那个:数一数组合后的状态是否都合法,非法组合存在就换成互斥字段。

示例拆解

实验区是同一个课程列表的两种实现,关键点如下:

  • 修复版:单个 status 字段(loading / error / empty / success),三个按钮分别触发「正常请求(500ms)」「模拟失败」「模拟空结果」;每种状态各有专属界面,重试按钮总能把状态拉回 loading;
  • Bug 版:loading 和 error 两个布尔各自为政;点「模拟失败」再点「正常请求」,请求期间 error 仍是 true,页面上失败和加载同时出现;
  • 每个分支都处理了边界:empty 不是报错,success 才渲染列表;
  • 切换状态按钮的代码能直接看出「每次进入新状态前,旧状态被显式覆盖」。

验证方式:在 Bug 版复现矛盾界面,再切到修复版做同样的操作序列,矛盾消失。

动手任务

打开本课实验区,完成三个小改动:

  1. 给修复版加第五种状态 offline(按钮「模拟断网」),确认加状态只需要改两处:状态赋值和渲染分支。
  2. 给错误分支加上失败原因文案(用 errorMsg state 保存)。
  3. 在 Bug 版复现至少一种非法状态组合,说出它对应哪两个布尔值。

常见错误

  • 多个布尔描述同一件事:loading/error/done 并存,组合出矛盾界面;改用一个互斥的 status 字段。
  • 切换状态时漏改旧状态:重试时只设 setLoading(true) 忘了 setError(false);互斥字段天然没这个问题。
  • empty 当 error 处理:空列表显示「出错了」误导用户;空结果属于成功分支,给专属的空状态界面。
  • 分支漏网:status 有五个值但只写了四个分支,未知值时页面空白;结尾留一个兜底分支(哪怕只是 return null 加注释)。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 拿到一个视图先数状态:把所有合法状态列全,确认互斥
  • 能说出「单布尔够用」和「必须互斥字段」的分界线
  • 写完分支后逐个状态点一遍,没有空白页和矛盾界面

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

购物车代码为 {cartCount && <CartBadge count={cartCount} />},当前 cartCount 为 0。页面上会发生什么?

{cartCount && <CartBadge count={cartCount} />}
Q2多选中等(多选,全部选对才算正确)

需求:status 为 'loading' 时显示 <Loading />,为 'error' 时显示 <ErrorTip />,其他情况显示 <Content />。下列哪些写法能正确实现?(多选)

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...