Thread
#compose
    Orhan Tozan

    Orhan Tozan

    1 year ago
    What is the recommended way to declare computed state? Is it with
    remember(a, b) { a + b}
    or
    derivedStateOf { a + b }
    ?
    At 9:05 of this video

    https://youtu.be/SMOhl9RK0BA

    ,
    val annotedText = remember(text) { parseAnnotated(text) }
    is being used. Any reason why remember is being used here instead of
    val annotatedText = derivedStateOf { parseAnnotated(text) }
    ? Is it simply because of text being a function param/prop instead of being state?
    Also have an extra question: why does derivedStateOf automatically know its dependencies, while we have to manually supply them with remember?
    lewis

    lewis

    1 year ago
    I believe
    derivedStateOf
    should be used to calculate a new
    State
    based on an existing
    State
    , in your example
    remember
    is the correct function.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    @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
    Dominaezzz

    Dominaezzz

    1 year ago
    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 🙂.
    lewis

    lewis

    1 year ago
    Interesting,
    derivedStateOf
    is more powerful than I realised.
    Dominaezzz

    Dominaezzz

    1 year ago
    We need to give remember the keys because it's not deriving state, it simply remembers and then forgets when the keys change.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    @Dominaezzz could you elaborate on why derivedStateOf needs to be remembered in order for it to be useful?
    Dominaezzz

    Dominaezzz

    1 year ago
    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.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    Oh yeah, I get it for this case, but for here:
    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 {}
    Dominaezzz

    Dominaezzz

    1 year ago
    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)
    Orhan Tozan

    Orhan Tozan

    1 year ago
    var a by remember { mutableStateOf(0) }
    var b by remember { mutableStateOf(0) }
    val sum = derivedStateOf { expensiveAlgorithmComputation(a, b) }
    How about now?
    Dominaezzz

    Dominaezzz

    1 year ago
    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.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    Let's make this example more close alike like the original example:
    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:
    @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)
    }
    Dominaezzz

    Dominaezzz

    1 year ago
    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. 😂
    Orhan Tozan

    Orhan Tozan

    1 year ago
    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.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    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) }````
    Dominaezzz

    Dominaezzz

    1 year ago
    Eager vs lazy.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    Remember = eager, derivedStateOf = lazy?
    Dominaezzz

    Dominaezzz

    1 year ago
    You can pass around the result of derivedStateOf without subscribing to the state. Which can be useful.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    Ok. any reason why we can't bring derivedStateOf power of automatically resolving its dependencies to remember?
    Dominaezzz

    Dominaezzz

    1 year ago
    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.
    Different use cases.
    A helper method could be nice though, I see what you're saying.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    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
    Dominaezzz

    Dominaezzz

    1 year ago
    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.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    How easy are we talking about?
    Dominaezzz

    Dominaezzz

    1 year ago
    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.
    Orhan Tozan

    Orhan Tozan

    1 year ago
    Ok, I thought derivedStateOf only works with state dependencies, but it apepars to work with everything
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    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.
    Sean McQuillan [G]

    Sean McQuillan [G]

    1 year ago
    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)