https://kotlinlang.org logo
#compose
Title
# compose
o

Orhan Tozan

02/26/2021, 11:10 AM
What is the recommended way to declare computed state? Is it with
remember(a, b) { a + b}
or
derivedStateOf { a + b }
?
Also have an extra question: why does derivedStateOf automatically know its dependencies, while we have to manually supply them with remember?
l

lewis

02/26/2021, 11:37 AM
I believe
derivedStateOf
should be used to calculate a new
State
based on an existing
State
, in your example
remember
is the correct function.
o

Orhan Tozan

02/26/2021, 11:42 AM
@lewis it is still interesting why we have to let
remember
know which parameters it depends on, while derivedStateOf does it out-of-the-box
For the same case,
text
could have been state, and
derivedStateOf()
would a been used, with no need for specifying that it depends on
text
d

Dominaezzz

02/26/2021, 11:45 AM
This is an eager vs lazy thing. derivedStateOf is lazy, which means of the inputs change the immediate lines of code afterwards doesn't have to be recomposed. Only the real dependencies have to be be.
derivedStateOf also has to be remembered otherwise it's not very useful 🙂.
💯 1
l

lewis

02/26/2021, 11:48 AM
Interesting,
derivedStateOf
is more powerful than I realised.
d

Dominaezzz

02/26/2021, 11:48 AM
We need to give remember the keys because it's not deriving state, it simply remembers and then forgets when the keys change.
o

Orhan Tozan

02/26/2021, 11:49 AM
@Dominaezzz could you elaborate on why derivedStateOf needs to be remembered in order for it to be useful?
d

Dominaezzz

02/26/2021, 11:50 AM
val annotated text = remember { derivedStateOf { parseAnnotated(text) } }
how it should be used.
If you don't remember the state object, then there's no caching. The state object would be recreated after every recomposition.
o

Orhan Tozan

02/26/2021, 11:52 AM
Oh yeah, I get it for this case, but for here:
Copy code
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
val sum = derivedStateOf { a + b }
I assume remember doesn't have to be used here. We dont have to let derivedStateOf know that it depends on a and b, in contrast to remember {}
d

Dominaezzz

02/26/2021, 11:55 AM
In that case, you don't need to let derivedStateOf know but you still need to hold a reference to the state object it returns. (I'm on my phone so I can't type code as much as I want to)
o

Orhan Tozan

02/26/2021, 11:56 AM
Copy code
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
val sum = derivedStateOf { expensiveAlgorithmComputation(a, b) }
How about now?
d

Dominaezzz

02/26/2021, 11:57 AM
Copy code
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
val sum = remember { derivedStateOf { expensiveAlgorithmComputation(a, b) } }
This is how it should be written.
o

Orhan Tozan

02/26/2021, 11:58 AM
Let's make this example more close alike like the original example:
Copy code
var text by remember { mutableStateOf("") }
val annotatedText = derivedStateOf { parseAnnotated(text) }
Why do you need
remember
here? The derivedStateOf docs says that it performs caching:
Creates a State object whose State.value is the result of calculation. The result of calculation will be cached in such a way that calling State.value repeatedly will not cause calculation to be executed multiple times, but reading State.value will cause all State objects that got read during the calculation to be read in the current Snapshot, meaning that this will correctly subscribe to the derived state objects if the value is being read in an observed context such as a Composable function.
Source: https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#derivedstateof
This is the example on the doc:
Copy code
@Composable fun Example() {
    var a by mutableStateOf(0)
    var b by mutableStateOf(0)
    val sum = derivedStateOf { a + b }
    // Changing either a or b will cause CountDisplay to recompose but not trigger Example
    // to recompose.
    CountDisplay(sum)
}
d

Dominaezzz

02/26/2021, 12:01 PM
Yes, the state object caches the result of the lambda but you still need to remember the state object and not recreate it.
The example is wrong. 😂
o

Orhan Tozan

02/26/2021, 12:03 PM
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
`val sum = remember { derivedStateOf { expensiveAlgorithmComputation(a, b) } }```` Your example looks reaaally complex compared to ReactJS simple usage of useMemo()
Could simplify it with a helper method.
o

Orhan Tozan

02/26/2021, 12:08 PM
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
`val sum = remember { derivedStateOf { expensiveAlgorithmComputation(a, b) } }```` Whats the point here actually of derivedStateOf? Is this not the same effect?:
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
`val sum = remember(a, b) { expensiveAlgorithmComputation(a, b) }````
d

Dominaezzz

02/26/2021, 12:12 PM
Eager vs lazy.
o

Orhan Tozan

02/26/2021, 12:14 PM
Remember = eager, derivedStateOf = lazy?
👍 1
d

Dominaezzz

02/26/2021, 12:14 PM
You can pass around the result of derivedStateOf without subscribing to the state. Which can be useful.
o

Orhan Tozan

02/26/2021, 12:17 PM
Ok. any reason why we can't bring derivedStateOf power of automatically resolving its dependencies to remember?
d

Dominaezzz

02/26/2021, 12:19 PM
What if you don't want that? What if you only want to recalculate based on one value but only take snapshots of other values that might change.
☝️ 1
Different use cases.
A helper method could be nice though, I see what you're saying.
o

Orhan Tozan

02/26/2021, 12:19 PM
Then introduce a new remember? E.g.
rememberWithMagic {}
Btw, I see there is also rememberUpdatedState, pretty confusing now 😂https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#rememberupdatedstate
d

Dominaezzz

02/26/2021, 12:23 PM
I reckon they won't add it, since it's easy for us to write it ourselves.
That's a fun one. Lots to learn here.
o

Orhan Tozan

02/26/2021, 12:23 PM
How easy are we talking about?
d

Dominaezzz

02/26/2021, 12:25 PM
Stupidly easy. Just make a composable function that takes a lambda. Remember and derive it. Done.
You may want to add overloads for the number of keys for remember but that's a different problem.
o

Orhan Tozan

02/26/2021, 12:26 PM
Ok, I thought derivedStateOf only works with state dependencies, but it apepars to work with everything
z

Zach Klippenstein (he/him) [MOD]

02/26/2021, 3:36 PM
The reason The reason
derivedStateOf
“automatically” knows its dependencies, but
remember
does not, is because of how snapshots work in compose. I recently tried to kind of explain how the snapshotting semantics work, and how it applies here is that
derivedStateOf
is a snapshot reader – after the first execution of that lambda, it will be notified any time a snapshot is applied that modifies any of the state objects in that lambda. This is super convenient, but comes at a price – it needs to do a lot more bookkeeping to track changes. It also returns a
State
instead of the raw value, which means: 1. The calling recompose scope doesn’t need to recompose if one of the dependencies change, only the scopes that are actually reading the state value do (e.g. if you call derivedStateOf in a composable but then only read the actual state value inside of composable lambdas passed to children). 2. The returned
State
object actually checks for modifications when the value is read. This is why it doesn’t need any explicit disposal.
remember
is much simpler and doesn’t do any bookkeeping, which makes it much cheaper to use. It returns the raw value returned from the lambda, which means that if it wants to return a new value (e.g. when one of its dependencies is changed), the entire calling recompose scope needs to be recomposed. It tracks dependencies not by using the snapshot mechanism, but instead by simply comparing the key values on each call.
s

Sean McQuillan [G]

02/26/2021, 7:14 PM
Back to the original question, this doesn't used
derivedStateOf
because the API doesn't exist when that presentation was made (it very well could if it was being written today - which would probably cause a different example to be chosen as that section was about introducing remember 🙂 ).
Zachs explination of the bookkeeping is spot on. TL;DR - remember is a cheap primitive for any object (even ones Compose knows nothing about) - derivedStateOf is a useful optimization for State<T> transformations (only)
4 Views