Compose Side Effects Quiz

UI › Compose Core

Why should composable functions avoid performing side effects directly in their body?

Answer: They can recompose often, run out of order, or be dropped unpredictably

Composables can recompose frequently, execute in any order, and be abandoned, so directly mutating external state runs unpredictably; effect APIs provide a lifecycle-aware place for it.

What happens to a LaunchedEffect's coroutine when one of its keys changes during recomposition?

Answer: The running coroutine is cancelled and a new one is launched

A key change cancels the currently running coroutine and launches a fresh one with the new keys; stable keys keep the same coroutine alive.

You need to show a snackbar when a Button is clicked. Which API launches the coroutine correctly?

Answer: Call rememberCoroutineScope(), then scope.launch { } in onClick

onClick is not a composable scope, so LaunchedEffect cannot be used there; rememberCoroutineScope gives a composition-aware scope, while GlobalScope is not lifecycle-bound.

An effect uses LaunchedEffect(Unit) with a delay, then invokes an onTimeout callback that may change across recompositions. How do you call the latest callback without restarting the timer?

Answer: Wrap onTimeout in rememberUpdatedState and call its value

rememberUpdatedState keeps a reference that is always current, so the long-lived effect calls the latest callback without restarting; adding it as a key would cancel and restart the delay.

Which statement about DisposableEffect is correct?

Answer: Its block must end with onDispose for cleanup on exit or key change

DisposableEffect is for effects needing cleanup; the mandatory onDispose block runs when the effect leaves composition or a key changes. Running after every recomposition describes SideEffect.

You want to send an analytics event the first time a LazyColumn is scrolled past its first item, using Flow operators. Which approach fits best?

Answer: Collect snapshotFlow { listState.firstVisibleItemIndex } in a LaunchedEffect

snapshotFlow turns the State read into a Flow you can transform with operators, collected from a coroutine; reading directly in the body would fire on every recomposition without Flow semantics.

What is the primary purpose of produceState?

Answer: To turn a Flow or suspend load into Compose State<T> for UI use

produceState launches a composition-scoped coroutine and exposes the result as State<T>, bridging external sources into Compose; publishing to non-Compose code is SideEffect's job.

You need to keep a non-Compose analytics SDK's current-user property in sync with Compose state. Which effect API is designed for this, and when does it run?

Answer: SideEffect, running after every successful recomposition

SideEffect runs after each successful recomposition and is meant for publishing Compose-managed state to non-Compose objects; running only after success avoids acting on a recomposition that gets discarded.

What is the fundamental distinction between using rememberCoroutineScope and LaunchedEffect to start a coroutine?

Answer: LaunchedEffect is composition-keyed; rememberCoroutineScope is for callback launches

LaunchedEffect ties a coroutine's lifecycle to composition and its keys (auto cancel and restart), whereas rememberCoroutineScope hands you a composition-scoped scope to launch from event handlers; both cancel when leaving composition.

Inside produceState, how do you clean up a listener you registered on a non-suspending, callback-based source?

Answer: Call awaitDispose { } at the end; it suspends until disposal, then runs cleanup

produceState runs a coroutine, so for a callback-based source you register the listener and then call awaitDispose, which suspends until disposal and runs its lambda; onDispose belongs to DisposableEffect.

Which statement best describes snapshotFlow?

Answer: A cold Flow that runs in a snapshot and conflates equal results

snapshotFlow is cold: on collection it runs its block, records which State objects were read, and re-emits when any of them changes, conflating equal consecutive results. It is typically collected inside a LaunchedEffect.

You collect a Flow from a ViewModel inside a LaunchedEffect to drive the UI. What is the recommended way to stop that collection while the app is in the background?

Answer: Use repeatOnLifecycle(STARTED) or collectAsStateWithLifecycle

A plain LaunchedEffect keeps collecting as long as the composable stays in composition, even in the background; repeatOnLifecycle (or collectAsStateWithLifecycle) ties collection to the STARTED state so it stops and restarts with the lifecycle.

A DisposableEffect has a key that changes during recomposition. In what order do its parts run?

Answer: The old onDispose runs first, then the effect block runs again with the new key

On a key change DisposableEffect first runs the old onDispose to clean up the previous registration, then executes the block again with the new keys.

What happens to the coroutine started by a LaunchedEffect when its call site permanently leaves composition?

Answer: It is cancelled, like a key change but with no restart.

LaunchedEffect scopes its coroutine to composition, so leaving composition cancels it with no restart, mirroring the cancellation half of the key-change behavior.

Back to Compose Side Effects