https://kotlinlang.org logo
Title
v

Vladas

03/22/2022, 11:13 AM
Found possible bug while trying to optimize my apps when using different declarations of
MutableState
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
Bad one. In this one composable recomposes every time counter is updated (each second)
@Composable
fun App() {
    val (counter, setCounter) = remember { mutableStateOf(1) }

    LaunchedEffect(null) {
        var i = 1
        while (true) {
            delay(1000)
            setCounter(++i)
        }
    }

    println("recomposition")
    val str by remember { derivedStateOf { (counter % 10 == 0).toString() } }
    Text(str)
}
This one works properly. Recomposes only when needed str changes. (every 10 seconds two times)
@Composable
fun App() {
    var counter by remember { mutableStateOf(1) }

    LaunchedEffect(null) {
        var i = 1
        while (true) {
            delay(1000)
            counter = ++i
        }
    }

    println("recomposition")
    val str by remember { derivedStateOf { (counter % 10 == 0).toString() } }
    Text(str)
}
Same behavior using android and js.
f

Filip Wiesner

03/22/2022, 11:18 AM
Reminds me of this Twitter thread
To quote:
val (foo, setFoo) = remember { mutableStateOf(...) }
is equivalent to
val state = remember { mutableStateOf(...) } 
val foo = state.component1() 
val setFoo = state.component2()
I don't really know how
derivedStateOf {}
works under the hood but in this case I believe you don't read
State
inside it, only the captured local value.
a

Albert Chang

03/22/2022, 11:28 AM
In the first snippet
counter
is not a state, so
derivedStateOf
won't work as you expected.
v

Vladas

03/22/2022, 11:29 AM
derivedStateOf
is optional here. Similar behavior without it. Just that in https://developer.android.com/jetpack/compose/state#state-in-composables quote: These declarations are equivalent, and are provided as syntax sugar for different uses of state.
a

Albert Chang

03/22/2022, 11:30 AM
For
derivedStateOf
to work properly, you need to read the state inside the lambda, and in the first snippet you are reading the state outside.
v

Vladas

03/22/2022, 11:32 AM
So yeah using it like this`val (foo, setFoo) = remember { mutableStateOf(...) }` might chop performance when state updates fast because of
val foo = state.component1()
state read calls
z

Zach Klippenstein (he/him) [MOD]

03/22/2022, 4:31 PM
Derived state isn’t particularly useful in these examples because the
Text
composable will already be skipped if the same string is passed in on a recomposition, so the only thing you’re saving is the cost of re-composing the
App
composable which is almost just as trivial as the body of your derived state anyway.