📐 状态提升 State Hoisting

无状态组件 · 有状态组件 · 提升哲学 Compose 状态管理 · 第4章
🧩 什么是状态提升 definition

状态提升 (State Hoisting) 是一种将可组合项内部的状态 “上移” 到调用方, 使得可组合项变成无状态,而父组件负责持有状态并向下传递。 核心原则:数据向下,事件向上。

父组件 (有状态) — 持有状态 子组件 A (无状态) 子组件 B (无状态) ⬆ 事件回调 (onEvent) ⬇ 状态 / 数据
⚖️ 无状态组件 vs 有状态组件 stateless / stateful

📦 有状态组件

内部使用 remember / mutableStateOf 自行管理状态。组件自包含,但难以复用和测试。

例:remember { var count by remember { mutableStateOf(0) } }

📤 无状态组件

状态由父组件通过参数传入,自身只负责展示和发送事件。纯 UI,可预测性高。

例:fun MyCounter(count: Int, onIncrement: () -> Unit)
对比维度有状态组件无状态组件 (状态提升)
状态位置组件内部 (remember)父组件 / ViewModel
可复用性低 (状态绑定)高 (纯展示)
可测试性较难 (需模拟状态)容易 (纯函数式)
数据流不透明单向数据流 (清晰)
典型场景简单、一次性组件复杂、共享状态
// 🔴 有状态组件 (不推荐提升)
@Composable
fun BadCounter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("点击: $count")
    }
}

// ✅ 无状态组件 + 状态提升
@Composable
fun StatelessCounter(count: Int, onIncrement: () -> Unit) {
    Button(onClick = onIncrement) {
        Text("点击: $count")
    }
}

// 父组件持有状态
@Composable
fun Parent() {
    var count by remember { mutableStateOf(0) }
    StatelessCounter(count = count, onIncrement = { count++ })
}
🌟 状态提升的优缺点 pros & cons

✅ 优点

  • 单一可信源 — 状态集中在父级,避免多副本不一致
  • 组件复用性高 — 无状态组件可任意组合
  • 可测试性 — 纯 UI 逻辑,无需模拟状态
  • 数据流清晰 — 单向数据流,易于调试
  • 状态共享 — 多个子组件可共享同一状态

⚠️ 缺点

  • 参数透传繁琐 — 多层嵌套时逐层传递 (可借助 DI / 组合局部)
  • 父组件职责过重 — 所有状态逻辑上浮,父组件可能膨胀
  • 增加模板代码 — 需要显式声明回调参数
  • 学习曲线 — 初学者可能不习惯“事件向上”模式

💡 何时使用状态提升?

  • 多个组件需要共享同一状态
  • 希望组件可复用、可预览、可测试
  • 状态逻辑需要被上层控制 (如 ViewModel)
  • 遵循 单向数据流 架构
📌

核心要义

“状态提升” 是 Compose 中实现 可复用、可预测 UI 的基石。 将状态“请”出组件,让组件回归“纯展示”,父组件成为状态的管家。 配合 ViewModel 使用,可构建健壮的生产级应用。