第1章 状态提升的核心原则
状态提升(State Hoisting)是将组件内部状态迁移到共同祖先组件中,通过 props 向下分发的模式。其复用性提升的根基在于三条核心原则:单向数据流、状态所有权与组件职责分离。本章阐述三者的定义、边界划分以及它们如何共同构建可复用组件体系。
1.1 单向数据流
单向数据流规定数据只能从父组件向子组件传递,子组件不直接修改父组件的状态。这种约束使得数据变更路径清晰可追踪,避免了双向绑定带来的调试困难。
- 数据源唯一:每次状态变更必然来自拥有该状态的组件。
- 变更可预测:数据流动方向固定,易于定位 bug。
- 子组件无副作用:子组件仅通过回调函数向父组件传递意图,不越权修改。
1.2 状态所有权
状态所有权明确了“哪个组件拥有该状态”。提升后的状态应归属于共同祖先,该祖先负责状态的创建、更新与销毁。所有子组件均为“只读消费者”。
所有权规则:若多个同级组件需要共享同一状态,则该状态必须上移至其最近的共同祖先。这一定位标准是状态提升行为的关键决策点。
| 场景 | 所有权归属 | 复用影响 |
|---|---|---|
| 只被单个组件使用 | 留在该组件内部 | 无提升必要 |
| 被兄弟组件共享 | 上移至父组件 | 提升后子组件变为纯展示,可复用 |
| 被跨层级组件共享 | 上移至更高级祖先或全局 | 需谨慎权衡,避免过度提升 |
1.3 组件职责分离
职责分离要求每个组件只承担一件事。状态提升后,父组件成为“智能容器”(管理状态与逻辑),子组件变为“哑组件”(仅根据 props 渲染 UI)。这种分离使得子组件脱离具体业务状态,方便在不同父组件中复用。
- 容器组件:负责状态生命周期、请求、事件处理。
- 展示组件:无内部状态,仅通过 props 和回调与容器交互。
- 边界清晰:展示组件不应引入任何状态管理依赖。
实践建议:先写出展示组件的纯渲染逻辑,再考虑需要哪些状态和回调,最后将状态提升到合适的容器组件中。自底向上的推导方式更容易保持职责分离。
原则协同:一个示例
以下代码展示了一个简单的计数器对:两个按钮组件共同展示同一计数,状态由父组件 CounterPair 持有,子组件 CountButton 通过 props 接收 value 和 onIncrement。
// 父组件:状态所有者
function CounterPair() {
const [count, setCount] = useState(0);
return (
<div>
<CountButton value={count} onIncrement={() => setCount(c => c + 1)} />
<CountButton value={count} onIncrement={() => setCount(c => c + 1)} />
</div>
);
}
// 子组件:纯展示,可供不同场景复用
function CountButton({ value, onIncrement }) {
return <button onClick={onIncrement}>{value}</button>;
}
该示例体现了三个原则:单向数据流(父→子传递 count)、状态所有权(父拥有 count)、职责分离(CountButton 不管理状态)。
重构落地步骤(预览)
在后续章节中,我们将展开完整的重构流程。第1章仅提供核心原则,后续将围绕以下步骤组织实践:
- 识别共享状态
- 确定共同祖先
- 提取状态至祖先
- 通过 props 传递状态与回调
- 验证单向数据流是否被破坏
验收标准
完成本章学习后,你应能:
- 用一句话复述单向数据流的含义。
- 说明状态所有权的决策依据(组件层级关系)。
- 区分容器组件与展示组件的职责边界。
- 在代码评审中识别出违背三大原则的模式。