I am trying to create a `derivedStateOf` like belo...
# compose
a
I am trying to create a
derivedStateOf
like below(
multiStateSheetState
is a
AnchoredDraggableState
which has offset in it) • Not using
density
in remember key
Copy code
val density = LocalDensity.current
val height = remember {
    derivedStateOf { 
        maxSize - with(density) { multiStateSheetState.requireOffset().toDp() }
    }
}
• Using
density
in remember as key
Copy code
val density = LocalDensity.current
val height = remember(density) {
    derivedStateOf { 
        maxSize - with(density) { multiStateSheetState.requireOffset().toDp() }
    }
}
which one is correct?
requireOffset
is backed by a state object so should not be used as key. This is my understanding but I am not sure about the
staticCompositionLocalOf
s
That's an interesting question. Intuitively I'd imagine that a
static
composition local is not in fact backed by mutableState, so your first option should not work. If you are willing to give it a whirl, try to make your own static composition local and then change it to see how this would behave. I'd imagine it just lets the entire thing below its provider recompose, which will mean for option 1 the
remember
will still be persisted so it will be showing stale data. But I am just guessing here, I do not actually know 😊
And if that is confirmed, you could also try
Copy code
val density = LocalDensity.current
val updatedDensity = rememberUpdatedState(density)
val height = remember {
    derivedStateOf { 
        maxSize - with(updatedDensity) { multiStateSheetState.requireOffset().toDp() }
    }
}
To see if that "fixes" it again. I believe it should.
a
The latter is correct. •
density
is just a plain (final) variable. No matter where its value came from, what the lambda captures is the variable itself, not its source. • So whether you need to specify
density
as a key depends on whether it's mutable (and whether its properties are backed by mutable states). It's not.
s
Albert, if density came from a normal
compositionLocalOf
, which as far as I understand is backed by MutableState internally, then the first option would also work, right? Or do I misunderstand something here?
a
No. What the lambda captures is the (initial) value of
LocalDensity.current
, not
LocalDensity.current
itself.
s
Well well well, you live you learn. Ty for that! So
Copy code
val density = LocalDensity.current
val updatedDensity by rememberUpdatedState(density)
val height = remember {
    derivedStateOf { 
        maxSize - with(updatedDensity) { multiStateSheetState.requireOffset().toDp() }
    }
}
Should also work though right?
a
Yeah that should also work. But density change rarely happens, so it's probably more efficient to just use it as a key.
s
Yeah I agree. I just wanted to make sure I understood that correctly at least 😅
a
Thanks @Stylianos Gakis @Albert Chang for the explanation, I will try to create a sample and see the behavior myself.
But density change rarely happens, so it's probably more efficient to just use it as a key.
density was just an example I could use other things as well like context or width But why using as key is more efficient?
s
Because practically it will never change, so it will be the same as if it wasn't even a key. And it won't have to be tracked by derivedStateOf
a
Ohh got.. let's say if this is a relatively fast change value then will there be any performance diff between your solution(where you are using
rememberUpdatedState
) and
remember(density)
one? Asking as you specifically mentioned
rememberUpdatedState
so I am think it might be better or performant or handles more case
I quickly read
rememberUpdatedState
I think it's use to convert non state value(like function param) to state value so that anything reading from it like can get the updated value
m
For something like this, I would say that this shouldn't use
remember
or
derivedStateOf
. Derived state is for when the child state is likely to change less often than what you are deriving from. The common example is whether something is visible based on the scroll state. Scroll state updates often, but the visibility of something inside it will only toggle at a given point. This makes it so recomposition (or layout or rendering) only happens when that boolean changes, and not when the scroll state happens. In this case, the value of height changes every time the requireOffset changes, so there's no benefit. The calculation for height is very quick, so I would imagine the mechanics of remember will add more overhead then just recalculating it.
a
Thanks for your input. Actually this example I constructed just for my question. In reality I am using derived state like below
Copy code
val anchorHeight = remeber {
    derivedStateOf { 
        val anchor = multiStateSheetState.anchors.closestAnchor(multiStateSheetState.offset, false)
        val anchorPosInDp =  anchor?.let {
                multiStateSheetState.anchors.positionOf(anchor).toDpUsing(density)
            } ?: maxSize
        maxSize - anchorPosInDp // these will be fixed set of values based on the number of anchors
    }
}