组件身份与状态保留
组件身份与状态保留 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
切到课时 2,编辑框里还留着课时 1 打了一半的字——这类「状态串门」的根源是 React 判定组件身份的方式。读完你应该能解释状态何时保留、何时重置,并用条件结构和 key 修复切换课时后编辑内容混用的问题。
问题场景
一个课时编辑器,顶部两个按钮切换课时 1 和课时 2,下面是编辑框:
const [lessonId, setLessonId] = useState(1);
const lesson = lessons[lessonId];
return (
<>
<button onClick={() => setLessonId(1)}>课时 1</button>
<button onClick={() => setLessonId(2)}>课时 2</button>
<LessonEditor lesson={lesson} />
</>
);
在课时 1 的编辑框里打个「草稿」,切到课时 2——「草稿」还在。这不是 bug 的 bug 很危险:你以为在改课时 2,其实改的是带着课时 1 草稿的同一个组件实例,保存后内容就串了。
核心解释
React 决定「状态保不保留」只看组件树上的位置和类型:同一位置渲染同类型组件,就当作同一个实例,state 原样保留——哪怕 props 已经从课时 1 换成了课时 2。LessonEditor 里的 draft 是组件内部 state,切课时只是换了 props,React 根本不知道你需要重置它。
修复手段有两种,按场景选:
一是用 key 显式声明身份。key 是组件身份的一部分:key 变了,React 认为这是一个新组件,卸载旧的、挂载新的,state 全部重来:
<LessonEditor key={lesson.id} lesson={lesson} />
每切一个课时,编辑框自动回到该课时的原始标题。key 在这里表达的业务含义是「这是另一个编辑对象」。
二是调整条件结构。如果你在同一个位置用 if 切换渲染不同组件(比如 <A /> 和 <B /> 二选一),类型不同身份就不同,状态天然分离。但如果两个分支渲染的是同一个组件名,React 会保留状态——这时唯一可靠的手段还是 key。
什么时候要保留状态?tab 切换表单、折叠面板这类场景,用户期望回来时内容还在——那就别加 key,让位置身份帮你保留。保留与重置都是工具,取决于业务语义。
示例拆解
实验区并排放了两个编辑器,行为刻意不同:
- 上面的
LessonEditor没加 key:切课时后 draft 保留,你在课时 1 打的字原样出现在课时 2 的编辑框里,还会显示「有未保存修改」——这就是要修的现场; - 下面的
LessonEditor加了key={lesson.id}:每切一次课时,draft 重置为当前课时标题,「未保存修改」标记消失; - 两个编辑器都有「还原」按钮,把 draft 拉回当前课时标题——注意上面的编辑器点「还原」也会变化,因为 props 换了;
- 验证方式:两边各打一个字,来回切换按钮,对比哪个编辑框串内容、哪个干净重置。
动手任务
打开本课实验区,完成三个小改动:
- 给上面的编辑器补上 key,验证草稿不再串位。
- 新增课时 3,确认 key 方案对任意数量课时都成立。
- 把切换按钮改成「返回上一课时不重置」的语义:用记录上次课时的方式,思考为什么单靠 key 做不到、需要在哪里另存草稿。
常见错误
- 把 key 当输错时的重置按钮滥用:每次渲染都换 key(比如
key={Math.random()})会让组件每帧卸载重建,输入焦点、动画、state 全毁;key 只该在「身份确实变了」时变化。 - 用 key 重置了不该重置的兄弟组件:key 加在父容器上,整棵子树一起重置;把 key 放到真正需要重置的那个组件上。
- 以为用 useEffect 监听 props 重置 state 就行:
useEffect(() => setDraft(lesson.title), [lesson.id])能跑,但界面会闪一帧旧值;身份问题用身份手段(key)解决。 - 列表 key 用 index,切换后状态串位:和本课同理——index 相同的位置被视为同一身份;列表项带内部状态时必须用稳定 id 做 key。
检查与复盘
完成下方检查题确认理解。自查清单:
- 能复述身份规则:同位置同类型保留,key 参与身份判定
- 遇到「编辑内容串位」能定位:找到需要重置的组件,加数据身份的 key
- 分得清业务上「该保留」和「该重置」的场景,并说清理由
参考资料
- React 官方文档:state 保留与重置 —— 位置、类型、key 三要素的完整规则
- React 官方文档:管理状态 —— 状态放在哪个组件、如何共享的全局视角
读完这一课了?
标记完成后,工作台会安排接下来的学习。