Compose Side Effects Interview Questions

UI › Compose Core

Walk me through how you decide which effect API to reach for: LaunchedEffect, DisposableEffect, or SideEffect. What's the actual distinguishing question you ask?

What a strong answer covers: The question is what kind of work it is and what cleanup it needs: LaunchedEffect for suspend or coroutine work scoped to composition, like network calls or timed animations, that should cancel automatically on leaving composition or a key change; DisposableEffect for imperative, non-suspending work that needs explicit teardown via onDispose, like registering a listener or receiver; SideEffect for publishing the latest Compose state to non-Compose code after every successful recomposition with no cleanup at all. The common trap is using LaunchedEffect(Unit) to do a one-off imperative registration when DisposableEffect's guaranteed onDispose is what correctness actually requires.

You've inherited a composable using LaunchedEffect(true) to kick off a coroutine meant to run exactly once, ever, for that composable's lifetime. What's wrong with that key choice, and does 'exactly once' even mean what you'd think?

What a strong answer covers: LaunchedEffect(true) restarts every time the composable re-enters composition, which happens if it's inside a conditional branch, a LazyColumn item scrolling back on screen, or a screen re-pushed by navigation without being retained in saved state, so 'once' really means once per entry into composition, not once ever. If truly once-per-app-lifetime semantics are needed, that state belongs in a ViewModel or SavedStateHandle that survives configuration change and re-entry, not in a composition-scoped effect.

Give me a scenario where rememberUpdatedState is necessary for correctness, not just an optimization, and explain what actually breaks if you omit it.

What a strong answer covers: Classic case: a LaunchedEffect(Unit) running a delay-based timeout that then calls an onTimeout lambda parameter which can change across recompositions, for example because the parent's handler closes over newer data. Since the effect's key is Unit, the coroutine captures that lambda reference once at launch and never updates it, so the effect ends up invoking a stale first version of the callback against outdated data, a real correctness bug, not wasted work. rememberUpdatedState wraps the changing value in a State the long-lived effect reads at call time, giving it the latest lambda without needing to include it in the key, which would otherwise restart the timer on every parent recompose and defeat the point of one continuous delay.

Back to Compose Side Effects