← All courses

Side Effects & the Compose Lifecycle

🗓 May 31, 2026 ⏱ 2 min read

Why side effects need special tools

A composable can recompose many times per second, in any order. So if you start a network call or a timer directly in its body, it could fire dozens of times or leak. Side-effect APIs let you run such work in a controlled, lifecycle-aware way.

LaunchedEffect — run when keys change

Runs a coroutine when the composable first appears, and again whenever a key changes. Perfect for loading data for a given id.

@Composable
fun Profile(userId: Int, vm: ProfileViewModel) {
    LaunchedEffect(userId) {
        vm.load(userId)        // re-runs only when userId changes
    }
    // ... UI ...
}

rememberCoroutineScope — launch from events

For coroutines started by a user action (a tap), not by entering the screen:

val scope = rememberCoroutineScope()
val snackbar = remember { SnackbarHostState() }

Button(onClick = {
    scope.launch { snackbar.showSnackbar("Saved!") }
}) { Text("Save") }

DisposableEffect — set up and clean up

When you must register something and then remove it (a sensor listener, a callback), use DisposableEffect with an onDispose block.

DisposableEffect(Unit) {
    val listener = LocationListener { /* ... */ }
    locationManager.register(listener)
    onDispose { locationManager.unregister(listener) }  // cleanup
}

rememberUpdatedState & derivedStateOf (briefly)

  • rememberUpdatedState — reference the latest value inside a long-running effect without restarting it.
  • derivedStateOf — compute a value from other state efficiently (recomputes only when inputs change).

Collecting state from a ViewModel

val uiState by viewModel.state.collectAsStateWithLifecycle()

Common mistakes

  • Starting coroutines or network calls directly in the composable body.
  • Using a constant key in LaunchedEffect when it should depend on changing data.
  • Forgetting onDispose cleanup in DisposableEffect (leaks).
Summary: Never run effects in the composable body. Use LaunchedEffect for work tied to keys, rememberCoroutineScope for event-driven coroutines, and DisposableEffect when you must clean up.