Android Compose UI Side Effects 副作用處理
在 Compose 的世界裡,Composable 函式最好是純函式 (Pure Function):輸入相同的參數,總是產生相同的 UI,且沒有副作用。
但現實世界充滿了副作用 (Side Effects):發送網路請求、啟動計時器、註冊接收器、彈出 Toast。如果不加控制,這些操作可能會在每次重組時被重複執行。
LaunchedEffect (一次性非同步任務)
當你想要在 Composable 進入畫面 (Enter Composition) 時 啟動一個 Coroutine。
@Composable
fun MyScreen(viewModel: MyViewModel) {
// key1 = Unit 表示只執行一次
LaunchedEffect(Unit) {
viewModel.loadInitialData()
}
}
如果 key 改變,原本的 Coroutine 會被取消,並重新啟動一個新的。
LaunchedEffect(userId) {
// 當 userId 改變時,重新 fetch 資料
viewModel.fetchUser(userId)
}
DisposableEffect (需要清理的任務)
當你需要在 Composable 離開畫面 (Leave Composition) 時 執行清理工作(如取消註冊 Callback)。
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { ... }
lifecycleOwner.lifecycle.addObserver(observer)
// 這行必定要寫!
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
SideEffect (發布 Compose 狀態到外部)
這比較少用。它是用來在重組成功後,將 Compose 的狀態「洩漏」給非 Compose 的系統(例如通知 Analytics 系統)。
SideEffect {
// 每次重組成功都會執行
analytics.logEvent("Screen Recomposed")
}
DerivedStateOf (衍生狀態)
當一個 State 頻繁改變(例如捲動位置),但你只關心它是否超過某個閾值時,使用 derivedStateOf 可以減少不必要的重組。
val listState = rememberLazyListState()
// 只有當 "是否大於 0" 的結果改變時,showButton 才會更新
val showButton by remember {
derivedStateOf { listState.firstVisibleItemIndex > 0 }
}
小結
正確管理 Side Effects 對於 App 的穩定性至關重要。記住:永遠不要在 Composable 的大括號內直接執行副作用操作,一定要包在 Effect API 中。