3. 实现 state + onEvent 模式:定义可观察状态、回调事件 lambda

3.1 原理:单向数据流的支柱

在 Compose 状态提升中,state 与 onEvent 构成一对不可分割的契约:

  • state —— 可观察的数据源,通常由 mutableStateOf() 创建,持有当前值并触发重组。
  • onEvent —— 回调 lambda,子 Composable 通过它向父组件发出“我想要改变状态”的信号,却不直接修改状态。

这种模式确保数据流向唯一:数据向下,事件向上。父组件拥有状态的唯一修改权,子组件只负责展示和触发回调。

3.2 实现流程

  1. 在父 Composable 中用 remember { mutableStateOf(initialValue) } 定义可观察状态。
  2. 将状态值作为参数传给子 Composable。
  3. 在父 Composable 中定义一个 lambda,用于更新状态(例如 { newValue -> state.value = newValue })。
  4. 将 lambda 作为另一个参数传给子 Composable。
  5. 子 Composable 在用户交互(如点击、输入)时调用该 lambda。

3.3 代码示例:计数器

@Composable
fun CounterScreen() {
    var count by remember { mutableStateOf(0) }  // 可观察状态

    // 事件回调 lambda
    val onIncrement: () -> Unit = { count++ }

    CounterDisplay(
        count = count,
        onIncrement = onIncrement
    )
}

@Composable
fun CounterDisplay(
    count: Int,
    onIncrement: () -> Unit
) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text("当前计数: $count")
        Button(onClick = onIncrement) {
            Text("增加")
        }
    }
}

在这个例子中:

  • CounterScreen 定义 count 和 onIncrement。
  • CounterDisplay 通过参数接收 count(只读)和 onIncrement(回调),不直接持有或修改状态。

3.4 角色对比

角色 Compose 对应 职责
State mutableStateOf() 持有可变数据,变化时自动触发重组
onEvent typealias OnEvent = (T) -> Unit 将子组件意图转发给父组件,不直接修改数据
父 Composable 自带 @Composable 定义状态、创建 lambda、向下传递
子 Composable 参数列表包含 state 和 onEvent 展示状态、在交互时调用 onEvent

3.5 调试与常见错误

  • ❌ 子组件直接修改传入的状态: 例如直接调用 count++ 会导致编译错误或不可预期行为。解决方案:必须使用回调。
  • ❌ 忘记包裹 remember: mutableStateOf 若未用 remember 包裹,重组时会丢失状态值。始终使用 remember { mutableStateOf() }。
  • ✅ 调试技巧: 在 lambda 中添加日志,观察回调触发次数;使用 LayoutInspector 查看重组范围。

3.6 性能与稳定性优化

  • 稳定 lambda: 用 remember 包裹回调,避免每次重组都创建新对象:val onIncrement = remember { { count++ } }。
  • 避免闭包陷阱: 如果 lambda 依赖外部变量,使用 snapshotFlow 或 derivedStateOf 减少不必要的回调创建。
  • 参数稳定性: 将状态拆分为细粒度 State 对象,只传递子组件真正需要的部分,减少重组范围。

3.7 state + onEvent 工作流

state + onEvent 模式工作流 Parent Composable var count by remember { mutableStateOf(0) } Child Composable CounterDisplay(count, onIncrement) state (count) onIncrement lambda State Update count++ → 触发重组 数据流向唯一 state 向下传递 → 子组件只读 → 事件向上传递 → 父组件更新状态 → 重组

3.8 总结

state + onEvent 是 Compose 状态提升的基石。通过明确分离状态所有权和事件响应的职责,代码变得可预测、可测试,并且天然支持 UI 的响应式更新。下一阶段我们会基于此模式构建更复杂的表单与列表交互。