How do you handle side effects in Jetpack Compose?
#jetpack-compose#side-effects#coroutines
Answer
Overview
Side effects are operations that escape the scope of a composable function.
Side Effect APIs
| API | Use Case |
|---|---|
| LaunchedEffect | Coroutines, async operations |
| DisposableEffect | Cleanup (listeners, subscriptions) |
| SideEffect | Update non-Compose state |
| rememberCoroutineScope | Launch coroutines outside composables |
| derivedStateOf | Expensive computations |
1. LaunchedEffect
kotlin@Composable fun LoadDataScreen(userId: String) { var user by remember { mutableStateOf<User?>(null) } LaunchedEffect(userId) { user = repository.fetchUser(userId) } user?.let { Text(it.name) } }
Cancellation
kotlinLaunchedEffect(key1) { // Cancelled when key1 changes or composable leaves composition val data = fetchData() updateUI(data) }
2. DisposableEffect
kotlin@Composable fun LocationUpdates() { DisposableEffect(Unit) { val listener = LocationListener { location -> // Handle location } locationManager.addListener(listener) onDispose { locationManager.removeListener(listener) } } }
3. SideEffect
kotlin@Composable fun AnalyticsScreen(screenName: String) { SideEffect { analytics.logScreenView(screenName) } }
4. rememberCoroutineScope
kotlin@Composable fun ScrollToTopButton(listState: LazyListState) { val scope = rememberCoroutineScope() Button( onClick = { scope.launch { listState.animateScrollToItem(0) } } ) { Text("Scroll to Top") } }
Common Patterns
Network Call
kotlin@Composable fun UserProfile(userId: String, viewModel: UserViewModel) { val user by viewModel.user.collectAsState() LaunchedEffect(userId) { viewModel.loadUser(userId) } user?.let { Text(it.name) } }
Timer
kotlin@Composable fun Timer() { var seconds by remember { mutableStateOf(0) } LaunchedEffect(Unit) { while (true) { delay(1000) seconds++ } } Text("Seconds: $seconds") }
Listener Registration
kotlin@Composable fun ConnectivityStatus() { var isOnline by remember { mutableStateOf(true) } DisposableEffect(Unit) { val callback = ConnectivityCallback { online -> isOnline = online } connectivityManager.registerCallback(callback) onDispose { connectivityManager.unregisterCallback(callback) } } Text(if (isOnline) "Online" else "Offline") }
derivedStateOf
kotlin@Composable fun TodoList(todos: List<Todo>) { val completedCount by remember { derivedStateOf { todos.count { it.isCompleted } } } Text("Completed: $completedCount / ${todos.size}") }
Rule: Use side effects APIs for operations outside Compose. Never perform side effects directly in composable bodies.