状态提升与共享
状态提升与共享 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
两个控件各管各的 state,数据就永远对不上。这一课讲状态提升:把多个组件共同依赖的状态搬到最近的公共父组件,让子组件变成「显示 props、上报变更」的受控形态。读完你能完成一次规范的提升改造,并说清判断「该不该提升」的依据。
问题场景
课程库页面有两个筛选控件和一个结果列表:搜索框自己存 query,难度下拉自己存 level,列表读这两个值做过滤。为了「互不影响」,每个控件都把自己的 state 藏在内部。然后需求来了:头部要显示「当前筛选出 N 门课」,底部要一个「清空筛选」按钮——它们都拿不到控件内部的 state。
核心解释
判断依据一句话:当一个状态被两个以上组件需要时,把它提升到它们的最近公共父组件。 state 留在组件内部的前提是「只有自己用」;一旦有人共享,私有 state 就成了数据孤岛。
提升分三步:
- 删掉子组件里的 state,换成两个 props:
value(当前值)和onChange(变更回调); - 父组件持有 state,往下传
value,往上传setXxx; - 子组件只显示、只上报——它不再拥有数据,输入框读
props.value,输入时调props.onChange。
function SearchBox({ value, onChange }) {
return (
<input
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder="搜索课程"
/>
);
}
function CourseLibrary() {
const [query, setQuery] = useState("");
return <SearchBox value={query} onChange={setQuery} />;
}
这种「组件不持有状态、完全由 props 驱动」的形态叫受控组件。它的价值在实验里看得见:搜索框、下拉、结果计数读的是同一个 state,任何一处变化,其余同步更新;「清空筛选」只是父组件里一句 setQuery("")。下拉框同理:
function LevelSelect({ value, onChange }) {
return (
<select value={value} onChange={(e) => onChange(e.target.value)}>
<option value="all">全部难度</option>
<option value="core">核心</option>
</select>
);
}
反过来,提升也有代价:父组件成了状态枢纽,改一处要顺着 props 打通两层。所以不是提得越高越好——只提升「确实被共享」的状态,只有一个使用者的交互状态(比如输入框聚焦与否)留在内部。这与 R11 的最小状态集是同一件事的两个方向:值要少(不存派生),位置要准(只放公共父级)。
示例拆解
实验区是同一个课程库的两种结构,关键点如下:
- 孤岛版:搜索框和难度下拉各自持有 state,列表自己再复制一份做过滤——输入「react」后,头部计数和列表对不上,因为它们读的是两份数据;
- 提升版:
App持有query和level,三个控件与列表全部受控;计数是courses.filter(...).length的派生值(R11 的规则继续生效); - 「清空筛选」按钮一行清两个 state,验证所有控件同步归零——孤岛版做不到;
- 受控输入的实现细节:
value+onChange成对出现,父组件是唯一写入者。
验证方式:提升版里在搜索框打字,计数、下拉、列表、按钮全部同时反应;孤岛版里对比同一操作,至少有两处不同步。
动手任务
打开本课实验区,完成三个小改动:
- 给提升版加一个「只看已完结」开关,把它也接入同一个状态源。
- 在孤岛版给列表和搜索框各加一次同步代码,体验维护两份数据的成本。
- 给「清空筛选」加一个条件:仅当有任何筛选生效时按钮才可点(用派生值判断)。
常见错误
- 提升到过高的层级:状态挂在 App 顶层,整个组件树跟着它的每次变化重渲染;提升到「最近」公共父组件就够了。
- 子组件偷偷留一份内部 state 当初始值:受控后又存
useState(props.value),props 更新它不跟,两份打架;受控组件不存副本。 - 只传 value 忘传 onChange:输入框打不了字——没有 onChange 的受控输入是只读的;两个 props 成对出现。
- 把每个子组件的 UI 细节状态也一并提升:悬停、聚焦这类只有自己用的状态留在内部,别把提升变成全局收容。
检查与复盘
完成下方检查题确认理解。自查清单:
- 判断新状态的去处时先问「谁会用」:多人共享才提升,单人使用留内部
- 受控组件的形态能默写:删内部 state,接 value + onChange
- 验证共享状态用「一处改、处处变」的交互实测,不靠脑补
参考资料
- React 官方文档:状态提升 —— 提升步骤与受控组件的官方教程
- React 官方文档:选择状态结构 —— 状态放哪一层的原则
读完这一课了?
标记完成后,工作台会安排接下来的学习。