Jetpack Compose — When should I use derivedStateOf?

Ben Trengrove
Android Developers
Published in
6 min readNov 29, 2022

--

derivedStateOf — a really common question we see is where and when is the correct place to use this API?

The answer to this question is derivedStateOf {} should be used when your state or key is changing more than you want to update your UI. Or in other words, derivedStateOf is like distinctUntilChanged from Kotlin Flows or other similar reactive frameworks. Remember that Composables recompose when the Compose state object they read, changes. derivedStateOf allows you to create a new state object that changes only as much as you need.

Let’s take a look at an example. Here we have a username field and a button that enables when the username is valid.

Initial state of username and submitEnabled

It starts off empty and so our state is false. Now when the user starts typing, our state correctly updates and our button becomes enabled.

But here is the problem, as our user keeps typing we are sending state to our button over and over again needlessly.

State updates after the user continues typing

This is where derivedStateOf comes in. Our state is changing more than we need our UI to update and so derivedStateOf can be used for this to reduce the number of recompositions.

Updating the code to use derivedStateOf

Let’s go through the same example again to see the difference.

State updates with derivedStateOf

The user starts typing, but this time our username state is the only one that changes. The submit state just remains true. And of course, if our username becomes invalid. Our derived state correctly updates again.

Now, this example is a bit oversimplified. In a real app, Compose would most likely skip recomposition of the submit composable as its input parameters have not changed.

The reality is, the situations when you need derivedStateOf can feel few and far between. But when you do find a case, it can be supremely effective at minimizing recomposition.

Always remember that there needs to be a difference in the amount of change between the input arguments and output result for derivedStateOf to make sense.

Some examples of when it could be used (not exhaustive):

  • Observing if scrolling passes a threshold (scrollPosition > 0)
  • Items in a list is greater than a threshold (items > 0)
  • Form validation as above (username.isValid())

FAQs

Now, let’s look at some other commonly asked questions about derivedStateOf.

Does derivedStateOf have to be remembered?

If it is inside a Composable function, yes. derivedStateOf is just like mutableStateOf or any other object that needs to survive recomposition. If you use it inside a composable function then it should be wrapped in a remember or else it will be reallocated on every recomposition.

What’s the difference between remember(key) and derivedStateOf?

Remember with keys of each state and derivedStateOf can seem quite similar at first glance.

The difference between remember(key) and derivedStateOf is in the amount of recomposition. derivedStateOf {} is used when your state or key is changing more than you want to update your UI.

Take for example enabling a button only if the user has scrolled a LazyColumn.

val isEnabled = lazyListState.firstVisibileItemIndex > 0

firstVisibleItemIndex will change 0, 1, 2 etc as the user scrolls and causes readers to recompose every time it changes. We only care about if it’s greater than 0 or not. There is a difference in the amount of input we have and output we need and so derivedStateOf is used here to buffer out that unnecessary recomposition.

val isEnabled = remember {
derivedStateOf { lazyListState.firstVisibleItemIndex > 0 }
}

Now, let’s say we have an expensive function that calculates something for us with a parameter. We want our UI to recompose any time the output of that function changes (importantly, the function is also idempotent). We use remember with a key here, as our UI needs to update just as much as our key changes. That is, we have the same amount of input and output.

val output = remember(input) { expensiveCalculation(input) }

Do I ever need to use remember(key) and derivedStateOf together? When is this needed?

This is where things get a little tricky. derivedStateOf can only update when it reads a Compose state object. Any other variables read inside derivedStateOf will capture the initial value of that variable when the derived state is created. If you need to use those variables in your calculation, then you can provide them as a key to your remember function. This concept is much easier to understand with an example. Let’s take our isEnabled example from before and expand it to also have a threshold for when to enable the button, rather than 0.

Here we have a button that enables when a list is scrolled over a threshold. We are correctly using derivedStateOf to remove the extra recomposition, but there is a subtle bug. If the threshold parameter changes, our derivedStateOf won’t take the new value into account as it captures the initial value on creation for any variable that isn’t a compose state object. As threshold is an Int, whatever is the first value that is passed into our composable will be captured and used for the calculation from then on. ScrollToTopButton will still recompose, as its inputs have changed, but as remember without any keys caches across recomposition, it will not reinitialise the derivedStateOf with the new value.

We can see this by looking at the outputs from our code. At first everything is working correctly.

But then a new value (5) for threshold is passed into our composable.

Even though our scrollPosition is less than threshold, isEnabled is still set to true.

The fix here is to add threshold as a key for remember, this will reinitialise our derivedStateOf state anytime threshold changes.

Now we can see, when the threshold changes, the isEnabled state correctly updates.

Do I need to use derivedStateOf to combine multiple states together?

Most likely, no. If you have multiple states that are combining together to create a result then you probably want recomposition to happen any time one of them changes.

Take for example a form that takes in a first name and last name and displays a full name.

Here, as our output changes just as much as our input, derivedStateOf is not doing anything and is just causing a small overhead. derivedStateOf also isn’t helping with asynchronous updates, the Compose state snapshot system is dealing with that separately and these calls here are synchronous.

In this case, there is no need for an extra derived state object at all.

Conclusion

To sum up, remember that derivedStateOf is used when your state or key is changing more than you want to update your UI. If you don’t have a difference in the amount of input compared with output, you don’t need to use it.

--

--