复杂状态综合实践
复杂状态综合实践 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
这是状态设计几课的综合实践:做一个带详情面板的任务看板。读完你应该能把列表、选中项、编辑草稿三类状态组合清楚,并把取消编辑、删除选中项、切换选中这些边界一次处理干净。
问题场景
任务看板左边是任务列表,右边是详情面板:选中某条看详情,点「编辑」改标题和描述,可以取消或保存,也能删除任务。随手写起来是三个 state:
const [tasks, setTasks] = useState(initialTasks);
const [selectedId, setSelectedId] = useState(1);
const [draft, setDraft] = useState(null); // null = 没在编辑
结构没问题,麻烦都在交互边界上:编辑到一半点了另一条任务,草稿跟着跑;删除当前选中的任务,详情面板显示什么?这些不处理,demo 一演示就露馅。
核心解释
先把三块状态的职责划清,再定边界规则:
- tasks:唯一数据来源,所有增删改都发生在它身上;
- selectedId:只存一个 id,详情面板用
tasks.find(t => t.id === selectedId)派生出选中的任务——不存任务对象,否则列表更新后面板会拿着旧对象各走各路; - draft:编辑草稿,
null表示没在编辑。它是「编辑会话」的状态,生命周期只属于当前这次编辑。
三条边界规则,对应三个经典事故现场:
切换选中,丢弃草稿。 用户点另一条任务时,旧编辑没有意义,直接 setDraft(null)。想做成「切换前提示保存」也行,但必须在 select 的入口处拦截,而不是散在列表渲染里。
删除选中,面板有归宿。 删除当前选中的任务后,selectedId 指向一个不存在的任务。规则要明确:清空选中、退出编辑,面板显示「未选中任务」的空态。绝不能让面板拿着已删除的任务继续编辑保存——那等于把它从回收站救回来写回列表。
取消编辑,等于丢弃草稿。 「取消」和「保存」的唯一区别是:取消不把 draft 写回 tasks。两者结束时都要 setDraft(null),别漏。
示例拆解
实验区看板的核心逻辑:
function select(id) {
setSelectedId(id);
setDraft(null); // 规则 1:切换选中丢弃草稿
}
function remove(id) {
setTasks(ts => ts.filter(t => t.id !== id));
if (id === selectedId) {
setSelectedId(null); // 规则 2:删除选中项后清空详情
setDraft(null);
}
}
function saveDraft() {
setTasks(ts => ts.map(t => (t.id === selectedId ? { ...t, ...draft } : t)));
setDraft(null); // 规则 3:保存后退出编辑
}
- 详情面板按状态渲染:未选中显示空态提示;选中且无 draft 显示只读详情和「编辑」按钮;有 draft 显示输入框和「保存 / 取消」;
- 已完成切换用
setTasks里 map 更新单项,选中状态不受影响; - 验证方式:选中任务 1 → 编辑 → 不保存直接点任务 2(草稿消失)→ 再编辑任务 2 → 删除它(面板回落空态,无报错)→ 取消再选中任务 1(详情完整)。
动手任务
打开本课实验区,完成三个小改动:
- 新增「新建任务」按钮:插入一条空任务并自动选中它进入编辑态。
- 删除任务前弹确认(用简单的 confirm 或两步按钮),确认后执行现有 remove 逻辑。
- 编辑标题为空时禁用「保存」,并在输入框旁提示原因。
常见错误
- 详情面板存整个任务对象:列表更新后面板还握着旧对象,保存一次就把旧数据写回去;只存 id,展示时派生。
- 删除后不清 selectedId:面板引用已删除任务,轻则显示错乱,重则「保存」凭空恢复一条死任务。
- 编辑态切换选中不丢弃草稿:草稿跟着 selectedId 走,改了 A 保存到 B;切换入口统一处理。
- 「取消」按钮只藏表单不清草稿:下次打开编辑又冒出上次的输入;取消和保存都必须显式结束编辑会话。
检查与复盘
完成下方检查题确认理解。自查清单:
- 三块状态(tasks / selectedId / draft)各自职责和派生关系说得清
- 三条边界规则(切换丢弃草稿、删除清选中、取消清草稿)都在对应入口实现
- 按实验区的验证路径完整走一遍:编辑中断线、删除选中、取消编辑都不出乱子
参考资料
- React 官方文档:管理状态 —— 状态归属与组合的整体思路
- React 官方文档:选择 state 结构 —— 为什么存 id 而不是对象引用
读完这一课了?
标记完成后,工作台会安排接下来的学习。