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

组件身份与状态保留

组件身份与状态保留 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

切到课时 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 换了;
  • 验证方式:两边各打一个字,来回切换按钮,对比哪个编辑框串内容、哪个干净重置。

动手任务

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

  1. 给上面的编辑器补上 key,验证草稿不再串位。
  2. 新增课时 3,确认 key 方案对任意数量课时都成立。
  3. 把切换按钮改成「返回上一课时不重置」的语义:用记录上次课时的方式,思考为什么单靠 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
  • 分得清业务上「该保留」和「该重置」的场景,并说清理由

参考资料

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

阅读代码(React 18+)。在输入框里输入「你好」后,点击「切换」按钮把 userId 从 1 改成 2。输入框里的文字会怎样?

function Profile({ userId }) {
  const [note, setNote] = useState('');
  return (
    <>
      <p>用户 {userId}</p>
      <input value={note} onChange={(e) => setNote(e.target.value)} />
    </>
  );
}

export default function App() {
  const [userId, setUserId] = useState(1);
  return (
    <>
      <button onClick={() => setUserId(2)}>切换</button>
      <Profile userId={userId} />
    </>
  );
}

读完这一课了?

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

Command Palette

Search for a command to run...