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

服务端数据与界面状态

服务端数据与界面状态 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完你能把一个页面的状态按来源分成三类——远程数据、筛选条件、临时选择——给每类安排合适的存放位置和更新节奏,消除「刷新数据把筛选和选择覆盖了」「两份缓存互相打架」这类问题。

问题场景

课程列表页第一版把所有东西塞进一个 useState 对象:

const [page, setPage] = useState({ courses: [], keyword: "", selected: [], loading: false });

问题接连出现:点「刷新」后 selected 被清空;两个组件各自拉了一份数据,筛选后互相对不上;想保留筛选条件刷新一下页面,全没了。根因是不同生命周期的状态被绑在同一个对象里,同生共死。

核心解释

先分类,再安排:

  • 远程数据(courses):来自服务端,生命周期跟「请求动作」走——只有请求结果可以写它,筛选、选择都不许直接碰;
  • 筛选条件(keyword、排序):用户意图,生命周期跟「页面会话」走,刷新数据时必须原样保留,适合放 URL 参数或提升到公共父组件;
  • 临时选择(selected、展开行):纯界面状态,生命周期跟「用户操作」走,通常留在使用它的组件里。

分类之后,数据流变成单向管道,展示用的列表是派生值,不进 state:

const [keyword, setKeyword] = useState("");           // 筛选条件:独立 state
const [courses, setCourses] = useState(null);         // 远程数据:只有 fetch 结果写它
const [selected, setSelected] = useState(() => new Set()); // 临时选择:独立 state

const visible = useMemo(
  () => (courses || []).filter((c) => c.title.includes(keyword)),
  [courses, keyword] // 展示列表 = 远程数据 × 筛选条件,现算
);

「刷新数据」只调 setCourses,keyword 与 selected 原封不动;「改筛选」只动 keyword,不触发重新请求(除非筛选属于服务端参数——那就让它成为缓存键的一部分,下一课展开)。避免重复缓存的口诀:同一份数据只存一次,其余全是派生;两处存同一份数据,迟早不一致。验证方式:刷新、筛选、选择三个操作各做一轮,确认每一步只有一个类别的值在变。

示例拆解

实验区课程列表带搜索框、刷新按钮和多选:

  • 顶部显示「已选数量」与状态写入日志,每次 state 被写都留痕;
  • 点「刷新课程数据」:600ms 后 courses 更新,keyword 与 selected 保持不变,日志只记一条「写 courses」;
  • 输入关键词:列表即时变化,不发请求、不动 courses;
  • 勾选课程:只动 selected;
  • 验证:交替做三个操作各两轮,检查日志——每步只应有一个类别被写入。

动手任务

打开本课实验区:

  1. 把 selected 改成「刷新时清空」(业务上合理的选择),写出你在哪里加的代码,并说明为什么不能在 setCourses 的同一行顺手清。
  2. 新增「只看 8 小时以上」开关,判断它属于哪一类状态,加到正确的位置。
  3. 故意把 courses 复制一份存进另一个 state 并在筛选时同步修改,制造一次不一致;再改回单源,总结教训。

常见错误

  • 把派生列表也存进 state:筛选结果、排序结果都是计算值,存了就多一个要同步的副本;用 useMemo 现算。
  • 刷新逻辑顺手重置其他状态:「刷新数据」和「回到初始筛选」是两个动作,混在一起用户的选择就丢了。
  • 写入时改远程数据的形状:请求层返回什么就存什么,裁剪、重排留给派生层,否则两处消费对不上。
  • 临时状态提升过高:selected 只有一个组件用,却放进全局,别的更新跟着重渲染;提升到「需要的最小公共祖先」为止。

检查与复盘

自查清单:

  • 能对页面每个 state 说出类别(远程 / 筛选 / 临时)和生命周期
  • 刷新、筛选、选择三个操作互不干扰,验证过
  • 同一份数据在页面上只有一处存储

参考资料

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

「订单列表页」需要维护:① 订单数据(来自接口)② 当前搜索关键字 ③ 详情弹窗开关。哪些属于「服务端数据」而不是「界面状态」?

Q2多选中等(多选,全部选对才算正确)

下列数据中,哪些应该交给「服务端数据管理层」(如 React Query 的缓存)而不是组件 useState?(多选)

读完这一课了?

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

Command Palette

Search for a command to run...