SideEffect 用于将 Compose 内部状态安全地同步到外部非 Compose 世界。执行时机Composition 成功提交后、下一次重组之前。如果组合失败,SideEffect 不会执行。
场景 1:将 Compose 状态同步到外部对象
@Composable
fun ChatScreen(viewModel: ChatViewModel) {
val messages by viewModel.messages.collectAsState()
val listState = rememberLazyListState()
SideEffect {
// 把滚动位置同步给 ViewModel 做持久化
viewModel.saveScrollPosition(listState.firstVisibleItemIndex)
}
}
场景 2:分析埋点
@Composable
fun ProductPage(product: Product, tracker: AnalyticsTracker) {
SideEffect {
tracker.logEvent("product_view", mapOf("id" to product.id))
}
}
注意事项
1、不要在 SideEffect 里修改 Compose State — 会导致无限重组循环
2、适合"读"操作,不适合"写"操作 — 它是同步出去,不是触发变化
3、如果需要异步操作,用 LaunchedEffect 而不是 SideEffect
源码
// 1. SideEffect.kt
@Composable
fun SideEffect(effect: () -> Unit) {
currentComposer.recordSideEffect(effect)
}
和 snapshotFlow 的对比
| 对比项 | SideEffect | snapshotFlow |
|---|---|---|
| 登记方式 | recordSideEffect() → changes 列表 | Snapshot.registerApplyObserver() |
| 执行时机 | Composition commit 成功 | Global Snapshot apply 成功 |
| 失败处理 | 组合失败 → 丢弃 | Snapshot apply 成功才回调 |
| 底层共同点 | 都依赖"记录→提交"模式,不是立即执行 | 都依赖"记录→提交"模式,不是立即执行 |
Snapshot 状态管理层 和 Composer UI 构建层
Composition 层 (构建 UI 树)
│
│ 依赖
▼
Snapshot 层 (管理状态读写)
Snapshot apply 发生在底层,Composition commit 发生在上层。一个改了状态值,一个改了 UI 树。
Snapshot apply 只是把数据标记为已确认,但 UI 树不一定刷新:
// 场景:快速连续点击
button.onClick {
count++ // → 触发 Snapshot apply(数据层)
count++ // → 又触发一次 Snapshot apply
}
// 但 Composition 只会在下一帧跑一次重组
// → 两次 Snapshot apply,一次 Composition commit
| 对比项 | Snapshot apply | Composition commit |
|---|---|---|
| 改了啥 | State 值(变量层面) | UI 节点树(LayoutNode 层面) |
| 所在的层 | Snapshot 系统 | Composer 系统 |
| 触发者 | MutableSnapshot.apply() | 帧回调 + Composer |
| 失败怎么办 | 几乎不会失败(纯数据合并) | 可能失败(异常/中断 → 丢弃整棵树) |
| 观察者 | registerApplyObserver | recordSideEffect |
| 典型案例 | snapshotFlow 在此回调 | SideEffect 在此回调 |

3091

被折叠的 条评论
为什么被折叠?



