theapache64
08/21/2021, 5:14 PMrememberUpdateState
? π€ Maybe βwith and without rememberUpdateState
βAlex Gabor
08/21/2021, 5:24 PMonReceive
it should call currentOnSystemEvent
instead of onSystemEvent
.Alex Gabor
08/21/2021, 5:28 PMrememberUpdatedState
should be used because the parameter onSystemEvent
can change after recomposition and you want the BroadcastReceiver to use the latest value without recreating it on recompositiontheapache64
08/21/2021, 5:32 PMrememberUpdateState
, but what I canβt understand is βhow would be the behaviour without itβ.
for eg. hereβs a sample from official docs.
Code A (official)
@Composable
fun LandingScreen(onTimeout: () -> Unit) {
val currentOnTimeout by rememberUpdatedState(onTimeout)
LaunchedEffect(true) {
delay(SplashWaitTimeMillis)
currentOnTimeout()
}
}
Code B
@Composable
fun LandingScreen(onTimeout: () -> Unit) {
LaunchedEffect(true) {
delay(SplashWaitTimeMillis)
onTimeout()
}
}
both A and B produce the same output even if the LandingScreen recomposed more than one time, right?Aditya Wasan
08/21/2021, 5:33 PMtheapache64
08/21/2021, 5:34 PMAditya Wasan
08/21/2021, 5:34 PMDoris Liu
08/21/2021, 5:34 PMLaunchedEffect
does an implicit capture of the onTimeOut
lambda. Without the rememberUpdatedState
, it'll be invoking the potentially outdated lambda.Aditya Wasan
08/21/2021, 5:35 PMHalil Ozercan
08/21/2021, 5:35 PMDoris Liu
08/21/2021, 5:37 PMtheapache64
08/21/2021, 5:38 PMonTimeout
changed during the delay
?Doris Liu
08/21/2021, 5:40 PMtheapache64
08/21/2021, 5:43 PMrememberUpdatedState
that produce different output for each πAditya Wasan
08/21/2021, 5:43 PMHalil Ozercan
08/21/2021, 5:44 PMfun main() = singleWindowApplication {
Top()
}
@Composable
fun Top() {
val counter by produceState(0) {
while(isActive) {
delay(10)
value += 1
}
}
val myLambda: () -> Unit = if (counter % 2 == 0) {
{ println("This is even: $counter") }
} else {
{ println("This is odd: $counter") }
}
Child(myLambda)
}
@Composable
fun Child(block: () -> Unit) {
LaunchedEffect(Unit) {
while (isActive) {
delay(100)
block.invoke()
}
}
}
Halil Ozercan
08/21/2021, 5:45 PMThis is even: 4
This is even: 12
This is even: 22
This is even: 29
This is even: 39
This is even: 48
This is even: 58
This is even: 67
This is even: 77
This is even: 87
This is even: 97
Halil Ozercan
08/21/2021, 5:45 PMHalil Ozercan
08/21/2021, 5:46 PMrememberUpdatedState
, these are the logs
This is odd: 3
This is even: 12
This is even: 22
This is even: 32
This is even: 42
This is even: 52
This is odd: 61
This is odd: 71
This is odd: 81
This is odd: 91
This is odd: 101
This is odd: 111
Doris Liu
08/21/2021, 5:47 PMHalil Ozercan
08/21/2021, 5:50 PMHalil Ozercan
08/21/2021, 5:51 PMRafs
08/21/2021, 5:52 PMLandingScreen
demo where the lambda is never changed without the rememberUpdatedState
call. Thanks @Halil Ozercan I think i'm getting the hang of it now.
val SplashWaitTimeMillis = 5000L
@Composable
fun RememberUpdatedState() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
var callback by remember {
mutableStateOf({
println("Hello from initial callback")
})
}
LaunchedEffect(Unit) {
delay(2000)
callback = {
println("Hello from new callback")
}
}
LandingScreen(callback)
}
}
@Composable
fun LandingScreen(onTimeout: () -> Unit) {
// This will always refer to the latest onTimeout function that
// LandingScreen was recomposed with
val currentOnTimeout by rememberUpdatedState(onTimeout)
// Create an effect that matches the lifecycle of LandingScreen.
// If LandingScreen recomposes, the delay shouldn't start again.
LaunchedEffect(true) {
delay(SplashWaitTimeMillis)
currentOnTimeout()
}
/* Landing screen content */
}
Doris Liu
08/21/2021, 5:54 PMval textToDisplay = counter.toString()
val myLambda: () -> Unit = { textToDisplay }
Child(myLambda)
Aditya Wasan
08/21/2021, 5:56 PMDoris Liu
08/21/2021, 6:01 PMtheapache64
08/21/2021, 6:02 PMHalil Ozercan
08/24/2021, 2:42 PMrememberUpdatedState
. It's rather complicated but I'll try to simplify it as much as I can:
β’ Inline content composables in a Text
needs to get their constraints before the whole text is measured.
β’ We pass these constraints using a lambda with a signature of () -> Constraints
.
β’ This has never been a problem because constraints for text almost never change in Android. Even if they do, inline content is rarely used.
β’ Image is an inline content. Desktop windows are easily resizable. Images never get the updated constraints because lambda goes stale
β’ Adding rememberUpdatedState solves this problem.Halil Ozercan
08/24/2021, 2:46 PMHalil Ozercan
08/24/2021, 2:47 PMrememberUpdatedState
theapache64
08/24/2021, 4:31 PMKefas
08/24/2021, 5:21 PM@Composable
fun Screen() {
var number by remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
delay(100L)
number = 1
}
Foo(number)
}
@Composable
fun Foo(number: Int) {
println("Recomposed with $number")
val currentNumber = number
Text(text = "$currentNumber")
LaunchedEffect(Unit) {
println("Start effect")
delay(200L)
println("currentNumber = $currentNumber")
}
}
Result:
Recomposed with 0
Start effect
Recomposed with 1
currentNumber = 0
I still don't get it.
β’ Why the Text
can get the latest value whereas the effect can't?
β’ If I use rememberUpdatedState
instead, the effect can get the latest value. Why is that? Isn't the value itself already a state?Halil Ozercan
08/24/2021, 5:55 PMnumber
is a state in Screen
composable and when it changes, compose runtime knows exactly where to update. However, Effects are scheduled when composition happens for the first time. As your effect never gets invalidated, it has a stale version of number
which is currentNumber
. currentNumber
is not a state in the Foo
composable. It resets at each recomposition but those recompositions doesn't really change anything for the scheduled LaunchedEffect
. Once that LaunchedEffect
runs, it reads the value from first composition.
On the other hand, when you use rememberUpdatedState
, number
becomes a state in Foo
composable context. Any effect that is scheduled from Foo
will read the correct state value when it's accessed.Halil Ozercan
08/24/2021, 5:56 PMFoo
composable and read the currentNumber as state.value
?Kefas
08/24/2021, 5:59 PMsend a State<Int> to@Halil Ozercan The result is 1 (the latest value)composable and read the currentNumber asFoo
?state.value
Halil Ozercan
08/24/2021, 6:01 PMKefas
08/24/2021, 6:11 PMState
objects are passed into parameters instead of its value.Halil Ozercan
08/24/2021, 6:13 PM