Effective Android

Share this post

Jetpack Compose Effect Handlers

newsletter.jorgecastillo.dev

Jetpack Compose Effect Handlers

How to write lifecycle-aware side effects in Compose

Jorge Castillo
Aug 11, 2022
13
Share
Share this post

Jetpack Compose Effect Handlers

newsletter.jorgecastillo.dev

What is a side effect?

A side effect is Anything that escapes the scope of the function where it is called. Here is an effect to keep some external state updated:

It initializes and updates a callback reference on an external object as a side effect of the composition. We don’t have control over when the effect runs (Composable functions can run multiple times), and it is never disposed.

We need to ensure that

  • Effects run on the correct lifecycle step

  • Effects can dispose resources when leaving composition

  • Suspend effects are cancelled when leaving composition

  • Effects that depend on an input that varies over time are automatically disposed/cancelled & relaunched every time it varies

These mechanisms are provided by the Effect Handlers.

Effect Handlers

Composables enter the composition when attached and leave it when detached. Between both events, effects run. Some effects can outlive this cycle and span across recompositions.

DisposableEffect

For an effect that requires cleanup before leaving.

  • Fired when entering and every time keys change

  • Disposed when leaving, and every time keys change (before re-trigger)

This handler attaches a callback to an external dispatcher when entering and disposes it when leaving. The dispatcher is used as a key to ensure dispose+restart if it changes. For spanning the same effect across recompositions instead of retriggering it you can pass a constant key: DisposableEffect(true) or DisposableEffect(Unit). DisposableEffect always requires at least 1 key.

SideEffect

“Fire on this composition or forget”. If the composition fails, it gets discarded. Used for notifying state updates to objects not managed by Compose.

  • For effects that do not require dispose

  • Runs after every successful composition/recomposition

  • Publishing updates to external states not managed by Compose

currentRecomposeScope

Not an effect handler, but interesting. Same than View invalidate. Can be used from any Composable function to invalidate the composition locally 👉 enforce recomposition.

⚠️ Anti-pattern. Rely on Snapshot state to drive recomposition.

Can be useful when using a source of truth that is not compose State. This presenter does not rely on Compose State, invalidation is done manually:

derivedStateOf

To derive some state from other state objects.

  • Useful when we have some mutable state and other states that need to be recalculated only when the original changes

  • An optimization: Avoids recalculating on every recomposition

Example extracted from the official docs.

rememberCoroutineScope

To create jobs that are considered children of the composition.

  • Runs suspended effects bound to the composition lifecycle

  • Scope cancelled when leaving the composition

  • Same scope across recompositions: all submited jobs cancelled when leaving

  • Useful to launch jobs in response to user interactions

Throttling on UI. Alike postDelayed for Views. Every time the input changes it cancels the previous job and posts a new one with a delay.

LaunchedEffect

Similar but for loading initial state when entering the composition instead of after user interaction.

  • Runs suspend effect when entering, cancels it when leaving

  • Cancels and relaunches the effect when key/s change/s

  • Also useful to span a job across recompositions

produceState

Syntax sugar on top of LaunchedEffect. When LaunchedEffect feeds a State.

Supports default value. Behaves the same as LaunchedEffect.

rememberUpdatedState

To capture a value in an effect & update it later without triggering it.

  • Useful for long lived operations that would be costly to recreate and restart

  • Useful when effect depends on a value that might change over time but we still want the effect to span across recompositions

Example extracted from the official docs.

Every time it is called it will update its value but will not retrigger effects reading from it.

3rd party library adapters

There are adapters for multiple well known libraries that delegate to the effect handlers.

You can find documentation for all the adapters available to convert 3rd party data types to Compose State here. There is also a collectAsStateWithLifecycle variant for collecting flows safely in Android. Finally, there is an adapter to convert from Compose Snapshot State to a cold Flow here.

All these adapters rely on effect handlers like DisposableEffect, LaunchedEffect or produceState to observe new values emitted from the original source and map them into a Compose State instance.

Consider becoming a free or paid subscriber.

13
Share
Share this post

Jetpack Compose Effect Handlers

newsletter.jorgecastillo.dev
Comments
Top
New
Community

No posts

Ready for more?

© 2023 Jorge Castillo
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing