derivedStateOf() – A definitive guide on when to use it

Remove the need for redundant recompositions

In this article, I will explain what derivedStateOf{} is and when it should, and shouldn’t be used. After reading this article, you will have a clear understanding of what it is used for.

Pre-requisites

To understand this article, you must be familiar with the following concepts:

  • Recomposition in compose.
  • Understanding of how state works in compose with regards to recomposition.
  • Knowledge of how to create and remember state in compose.

For the people who are familiar with Kotlin Flows 🌊

Photo by kazuend on Unsplash

This section is for the people who know to work with Kotlin flows. Feel free to skip this section if you don’t have experience using flows. If you know to work with flows, then understanding derivedStateOf{} is extremely easy. It is basically a distinctUntilChanged() operator, but, for compose state. That’s pretty much it! Since updates to state that are of the same value as the previously assigned value doesn’t cause the state to change when using derivedStateOf{}, the composables that read the state, don’t recompose.

When to use derivedStateOf?

Consider using derivedStateOf{} when you have state values that change more frequently than it is required for the composables that read it to recompose.

I don’t want you to skim past the statement that I just mentioned. I want you to read the sentence again slowly and try to understand it instead of just skimming through it. One additional advantage of using derivedStateOf{} is that, you do not need to necessarily specify any keys for the remember{} block. The “calculation” block of derivedStateOf{} will automatically re-execute whenever a state that is read inside the block changes. If you want the block to be executed when a state that is not used in the calculation block of derivedStateOf changes, then pass them as keys to the remember block.

@Composable
fun someComposableFunction(){
.
.
.
var stateVariable by remember {
derivedStateOf {
// any state that is read within this block will cause
// this block to be re-executed when that state changes value.
}
}
.
.
.
}

Now that you understood when to use it, I will give you an example that will solidify your understanding of derivedState{}. But before that, let’s take a look at why we have to use it.

Why use derivedStateOf{}?

As we know, a recomposition gets triggered whenever the state changes. By default, this also holds true if the value of the state is changed to the same value. For example, let’s say we have a state of type Boolean, which is initially false. If the value of the state is set to false again, then the composables that read that state recompose even though the state was set to a value that it already had. To prevent this, we can use derivedStateOf{}. If the state is being updated to a value that it is already assigned to, then the state wouldn’t update, resulting in no recompositions. This is why you have to use derivedStateOf{}.

An example

The example that I’m about to give might be a cliché at this point because every video/article that I’ve ever seen regarding derivedStateOf{} usually uses this example. But it is a very good example to understand it.

Let’s say we want to display a “scroll to top” FAB (Floating Action Button) when the user has scrolled past the 10th item in a lazy list. We might define a variable of type Boolean that uses lazyListState.firstVisibleItemIndex to determine whether the FAB must be visible.

@Composable
fun someComposableFunction(){
.
.
var isScrollUpFabVisible = remember(lazyListState.firstVisibleItemIndex){
lazyListState.firstVisibleItemIndex > 10
}
.
.
if(IsScrollUpFabVisible){
/* Display the floating action button*/
}
.
.
}

This works, but there is a major performance issue associated with defining such a variable. The lazyListState.firstVisibleItemIndex property changes very frequently. This makes sense because the index of the first visible item keeps changing as the user scrolls the list. This results in the entire composable, in this case “someComposableFunction”getting recomposed whenever the user scrolls!

A visualization would be very helpful to understand what’s happening when we use a variable like this. The table below shows how the previous and computed value of the state changes as the user scrolls. The computed value refers to the value that is yielded as a result of the re-execution of the “computation” block of derivedState{}. Remember, the “computation” block re-executes when a state value that is read inside the block changes or the containing remember{} block gets re-executed because one of its keys changed value.

Recomposition behavior when derivedStateOf is not used

As you can see, although all values below 11 always have a computed value of false, the composable still gets recomposed every time the state changes. As mentioned earlier, the value of the state changes even though the computed value of the state is the same as the old value. This results in the composable getting recomposed. Similarly, this also happens when the firstVisibleItemIndex is above 11. The only difference being the existing and computed values will always be true. Ideally, the composable should only recompose when the firstVisibleItemIndex changes from 10 to 11. Now, let’s see what happens when we use derivedStateOf{}.

@Composable
fun someComposableFunction(){
.
.
var isScrollUpFabVisible by remember {
derivedStateOf { lazyListState.firstVisibleItemIndex > 10}
}
.
.
if(IsScrollUpFabVisible){
/* Display the floating action button*/
}
.
.
}

If we conceptualize the state updates as a stream, then subsequent values that are of the same value must be ignored. For example, if the stream of state values are like this — false, false, true, true, true. All the false values that appear after the first false value and all the true values that appear after the first true value must be ignored. This is exactly what derivedStateOf{} does. It compares the existing value to the newly computed value. It will update the state only if the computed value is different from the existing value. As a result, the composable will recompose only when the state changes to a new value. Let’s visualize what happens when we use derivedStateOf{}.

Recomposition behavior when derivedStateOf is used

As you can see, the composable gets recomposed only when the state changes from false to true. This will also be true the other way around. This is much better that recomposing the composable whenever the index of the first visible item changes. This is exactly when you have to use derivedStateOf{}.

When not to use derivedStateOf?

Now that we know when to use it. Let’s see when not to use it. We shouldn’t use derivedStateOf{} when a composable must recompose as frequently as the state changes. As a matter of fact, using derivedStateOf{} actually results in a little overhead if it is used in such cases.

For example, let’s say we have two state variables that hold the first and last names respectively. Now, let’s say our imaginary designer asked us to display them in a combined manner as the user is typing. We might be tempted to do something like this.

@Composable
fun someComposableFunction(){
.
.
var firstName by remember { mutableStateOf("") }
var lastName by remember { mutableStateOf("") }
val combinedName by remember {
derivedStateOf { firstName + lastName }
}
.
.
/* Display the combinedName*/
.
.
}

Since the UI has to be updated every time either of the two state variables changes, it would be redundant to use derivedStateOf{} here.

Conclusion

That wraps up this blog post🎉! Hope you understood what derivedStateOf{} is and what it is used for. If you have any doubts, feel free to send them my way! With that, I would like to thank you for taking your time reading this article😊. Happy coding 👨‍💻!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
The Android Developer

The Android Developer

60 Followers

A very passionate Android Developer 💚. An extreme Kotlin fanatic 💜. Focused on making quality blog posts 📝.