Interview QuestionPractical QuestionFollow-up Questions

Side Effects in Jetpack Compose

skydovesJaewoong Eum (skydoves)||8 min read

Side Effects in Jetpack Compose

A side effect in Jetpack Compose is any operation that escapes the scope of a composable function and persists beyond recomposition. Composables are intended to be pure functions of their inputs: given the same state, they should produce the same UI. When you need to interact with external systems, launch coroutines, register listeners, or synchronize with non Compose state, Compose provides dedicated side effect APIs that integrate these operations with the composition lifecycle. By the end of this lesson, you will be able to:

  • Define what constitutes a side effect in the context of Compose recomposition.
  • Explain how LaunchedEffect ties coroutine lifecycle to composition entry and key changes.
  • Describe how DisposableEffect manages resource setup and teardown.
  • Apply SideEffect for synchronizing Compose state with external systems after recomposition.
  • Choose the correct side effect API based on the operation's requirements.

LaunchedEffect and Coroutine Scoping

LaunchedEffect launches a coroutine that is scoped to the composition. The coroutine starts when LaunchedEffect enters the composition and is cancelled when it leaves. If the key parameter changes, the existing coroutine is cancelled and a new one is launched.

@Composable
fun UserProfile(userId: String) {
    var user by remember { mutableStateOf<User?>(null) }

    LaunchedEffect(userId) {
        user = repository.fetchUser(userId)
    }

    user?.let { Text(it.name) }
}

The key parameter (userId) determines when the effect restarts. When userId changes, the previous coroutine fetching the old user is cancelled and a new coroutine starts for the new user. If userId stays the same across recompositions, the coroutine continues undisturbed. This makes LaunchedEffect the right choice for async operations that depend on specific inputs.

Using Unit as the key means the effect runs once when the composable enters the composition and never restarts:

LaunchedEffect(Unit) {
    analytics.logScreenView("home")
}

The coroutine runs in the Composition's coroutine context, which uses Dispatchers.Main by default. You can switch contexts inside the block using withContext for background work.

DisposableEffect and Resource Cleanup

DisposableEffect handles operations that require both setup and cleanup. It takes a key parameter (like LaunchedEffect) and provides an onDispose block that runs when the effect leaves the composition or when the key changes and the effect restarts.

@Composable
fun LocationTracker(locationManager: LocationManager) {
    val callback = remember { LocationCallback() }

    DisposableEffect(locationManager) {
        locationManager.requestLocationUpdates(
            LocationRequest.create(), callback, Looper.getMainLooper()
        )
        onDispose {
            locationManager.removeUpdates(callback)
        }
    }
}

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor