I’m trying to understand derivedStateOf better. De...
# compose
p
I’m trying to understand derivedStateOf better. Depending on the list state, I want to determine if the toolbar should be transparent or not. Which version is correct?
transparent
or
transparent2
?
Copy code
@Composable
public fun TransparentTopAppBar(
  contentListState: LazyListState,
) {
  val transparent by remember {
    derivedStateOf {
      contentListState.firstVisibleItemIndex == 0 && contentListState.firstVisibleItemScrollOffset == 0
    }
  }
  val transparent2 by remember(contentListState) {
    derivedStateOf {
      contentListState.firstVisibleItemIndex == 0 && contentListState.firstVisibleItemScrollOffset == 0
    }
  }
}
h
Or do you ask because you pass a state?
p
The docs use the first variant
s
At first glance, I’d say 2nd version is correct, since contentListState is a simple variable at that point, and derivedStateOf wouldn’t know to re-calculate itself if it changes, it only does so when reading
State
. But inside the derivedStateOf here, it’s reading
firstVisibleItemIndex
and
firstVisibleItemScrollOffset
which both are backed by MutableState inside the
LazyListScrollPosition
class, so derivedStateOf should be notified about those changes. So maybe if contentListState changes,
derivedStateOf
would normally not get notified, but since it’s reading some `MutableState`s from that object itself, it still turns out to be notified as it should. One thing one could do to test this out is to make a sample where you pass in a new LazyListState, and try it once where
firstVisibleItemIndex
and
firstVisibleItemScrollOffset
are the same, and once when they’re different, and see if it works properly. I can’t say for sure if I’d be confident that it does 🤷‍♂️
If it were me, I’d simply go with #2 since I know that it’d behave correctly, and all things considered, LazyListState won’t change nearly as much as the other two, so worst case scenario is I re-evaluate this once in a blue moon. So it’d keep my mind at peace, while not really sacrificing performance considerably.
a
The first one is incorrect. The initial
contentListState
instance will be captured by the lambda of
derivedStateOf
and will never change even if the argument changes. No matter
derivedStateOf
is notified or not, it will always calculate the value using the old
contentListState
instance. The doc is using the second one.
s
Awesome, thanks for the clarification Albert, so I was wrong in assuming that maybe derivedStateOf will pick it up. Sometimes it’s a bit tricky to understand this since derivedStateOf seems to magically capture the state that it needs to, so assumed it may be doing some tricks to make this work. But it doesn’t, and that’s good tbh, more clear to understand. Paul, if you think that you’ve found a place in the docs which uses #1 still, maybe link it here and make a bug report to have it changed
p
@Albert Chang The docs are using the first. They do not declare todoTasks as a key of remember
a
And that's because
todoTasks
never changes as it's not an argument.
s
Plus it’s a mutableStateList, so derivedStateOf does in fact know when its contents change, and will re-calculate as a result.