Compose Tip: Using LaunchedEffect and derivedStateOf for Effective UI State Management

Firuze Gümüş
4 min readNov 7, 2024

--

When I work with an API, I like to check out how it’s actually written — it helps me see why it’s built that way and where it works best. With that in mind, let’s take a closer look at LaunchedEffect and derivedStateOf in Compose. Understanding their inner workings can show us when they’re ideal for managing UI state smoothly.

Implementation of LaunchedEffect

Let’s start by looking at how LaunchedEffect is implemented within the Compose library:

In Compose, LaunchedEffect is built to launch a coroutine that ties directly to the lifecycle of the Composition. When you call LaunchedEffect, it uses Compose’s currentComposer to access the active Composition, allowing it to manage when elements should update or rerun. So, the currentComposer is a structure within Compose that keeps track of which composable functions are currently active and manages when each one should be created, drawn, or disposed. In this way, Compose can control the coroutine’s behavior according to the UI lifecycle, so it won’t keep re-launching unnecessarily.

applyCoroutineContext is another critical piece here. It’s part of currentComposer and sets up a CoroutineContext that syncs the coroutine’s lifecycle with Compose’s Composition. Essentially, it aligns the coroutine with the Composition lifecycle, ensuring that the coroutine created in LaunchedEffect is only launched once per key, even if recompositions occur. This makes it ideal for tasks that should run just once per state change.

When to use LaunchedEffect?

LaunchedEffect is particularly useful for starting background tasks that should run only once during the composable’s lifecycle or when a specific state changes. It’s perfect for operations like network requests on initial load or one-time animations, as it offloads these tasks to the coroutine, keeping the UI responsive.

Usage Example:

Imagine you’re building a UI that shows a list of messages. When a new message is received, you want to scroll to the bottom of the list to show the latest message. LaunchedEffect is useful here, as you can trigger it to run only when a new message arrives.

Implementation of derivedStateOf

Now, let’s look at how derivedStateOf is implemented within the Compose library:

In this implementation, derivedStateOf creates a DerivedSnapshotState based on the provided calculation, recalculating only when its dependent states are updated. If you need state that recalculates only when specific values change, derivedStateOf is your go-to. Unlike LaunchedEffect, derivedStateOf doesn’t trigger actions, it’s used to derive new states from other states in a way that reduces unnecessary recompositions. When defined, it creates a DerivedSnapshotState that recalculates only when its dependencies update.

When to Use derivedStateOf?

derivedStateOf is ideal for scenarios where you want to derive new states based on other states without unnecessary re-renders, making it highly efficient for UI elements that don’t require continuous recomputation.

Usage Example:
Lets imagine you’re building a UI with a list of items and a search box, and you want to filter the list based on the user’s input. If the filter function is computationally expensive, you want to avoid calling it on every recomposition. You can use derivedStateOf to compute the filtered list only when the search query or the original list changes.

Quick Recap

Both LaunchedEffect and derivedStateOf are essential tools in Compose for efficient state management, each suited for different scenarios.

LaunchedEffect is your best friend for triggering actions that should only happen once or need continuous monitoring aligned with the lifecycle, such as making a network request when a screen loads or running a one-time animation. By controlling when the coroutine launches, it prevents unnecessary re-runs, keeping the UI light and responsive.

derivedStateOf is ideal for conditions that should update only when specific state values change. It’s the perfect choice when you want to avoid unnecessary recompositions, like enabling a button only after a user has met a certain requirement, without monitoring constantly.

These are two features I’ve found particularly useful in my own projects, so I wanted to share my insights here. I hope this post provides value and can be of help in your projects as well!

--

--