Compose Chronicles: 🪄 State of the Art — produceState, derivedStateOf & snapshotFlow

Jesseosile
5 min readJun 18, 2024

--

Greetings once again, my fellow composers! 👋

Welcome back to the Compose Chronicles 🥳🥳🥳. We’ve come a long way with Side Effects and Effect Handlers, and while it’s been fun, all good things must come to an end 😭😭.

This is the final chapter of the Compose Chronicles. We’ll be closing things off with a strong finish, exploring produceState, derivedStateOf and snapshotFlow.

Without further ado, let’s get right into it! 😁.

What is produceState 🧐?

produceState is an effect handler that simplifies state management by creating state from non-composable code or asynchronous operations. It's particularly useful for fetching data from sources like local storage or networks.

When using produceState, a coroutine scoped to the composition is launched. This means the coroutine's lifetime is tied to the lifecycle of the composable, allowing you to make suspend calls within the coroutine.

produceState in Action!!! 🚀

We have a composable function called produceDiceState. The responsibility of this function is to loop through 1 to countUpTo(any positive integer). For each value in loop’s range, there will be a delay of one second before the value is wrapped in composable state and returned.

produceState takes a couple of parameters:

  • initialValue: This the initial state value that will be returned by produceState, It is a necessary value that must be passed.
  • keys: This can be any number of values. When this value changes a running producer will be cancelled and re-launched.
  • producer: The producer is a suspend function (uses coroutines) that acts as the brains behind produceState. It defines the initial state value and handles state updates based on your custom logic within the lambda.

MainScreen is a simple screen that displays a dice face based on the current dice state. countUpTo is set to 6 (meaning we loop from 1 to 6 in produceDiceState). The Image composable displays a dice face based on the state returned from produceDiceState, it shows a warning icon if the number returned exceeds 6.

produceState app Demo.

What is derivedStateOf 🧐?

It is a helper function that creates composable state that is derived from some other state. It comes in handy in cases where input state changes frequently, but your composable doesn’t need to update every single time the input changes. Instead, it only needs to update when certain conditions are met or thresholds are crossed.

derivedStateOf in action!!! 🚀🚀

DerivedStateScreen without derivedStateOf

We have a simple screen DerivedStateScreen, it takes a TextField and a custom button SideEffectButton. The button becomes enabled only the text entered into the TextField is valid (isTextValid function ensures that the text is not empty and has a length greater than or equal to 8).

When we run the app, it works fine, the button becomes enabled when the conditions are met. Buuuuutttttttttttt!! 😬

Every time the text value of the TextField changes, isEnabled get’s updated i.e. Every time “f” was entered isEnabled got set to “false” up until the conditions were met for a valid input did it change to true.

This isn’t ideal!

isEnabled should change only when the value is distinct (if it’s value at that point in time is “false”, it should only be updated when the new value coming in is “true”).

derivedStateOf to the rescue!!! 🦸‍♂️

DerivedStateScreen with derivedStateOf

When we run the app this time, it still looks and works the same but with a slight difference…it’s more performant!!!! 😁

This time isEnabled only get’s updated when the new value is distinct (if it’s false it only get’s updated when the new value is true.). This works like distinctUntilChanged Kotlin Flows. It’s super helpful because it avoids unnecessary recompositions.

NOTE: derivedStateOf does not necessarily mean the combination of multiple states to “derive” a new state.

In a situation like that 👆, it’s better to do this 👇:

what is snapshotFlow 🧐?

It is a function that converts a composable state into a cold flow (This flow starts emitting values only when it has active collectors/subscribers). It takes a “snapshot” of the current value of the composable state each time the state changes and emits this snapshot.

As a result, every time the composable state updates, a new value is emitted from the flow, allowing collectors to react to these changes.

snapshotFlow in action!!! 🚀🚀

SnapshotFlowScreen is a simple screen That has a Button. Each time this Button is clicked, it’s text it updated to reflect the change.

When the app first runs, a LaunchedEffect block is executed once. Within this block, snapshotFlow creates a cold flow that emits a new value of count each time it changes.

We collect the latest value from the snapshotFlow using collectLatest and then proceed to updating the text variable state with the new value of count.

snapshot flow in action

And that’s a wrap for the Compose Chronicles 😭😭!! It’s been a fun ride exploring different effect handlers in Jetpack Compose, thank you for reading and see you on the next one.

You can read my past articles here:

--

--