jetpack compose 副作用 SideEffect

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 的对比

对比项SideEffectsnapshotFlow
登记方式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 applyComposition commit
改了啥State 值(变量层面)UI 节点树(LayoutNode 层面)
所在的层Snapshot 系统Composer 系统
触发者MutableSnapshot.apply()帧回调 + Composer
失败怎么办几乎不会失败(纯数据合并)可能失败(异常/中断 → 丢弃整棵树)
观察者registerApplyObserverrecordSideEffect
典型案例snapshotFlow 在此回调SideEffect 在此回调
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值