On JS and Wasm (and maybe iOS), when depending a `val` storing the state value outside in a `NavHost` `composable` lambda, the updated state is not propagated #4966
val (count, setCount) = remember { mutableStateOf(0) }
val navController = rememberNavController()
val clickToIncCountModifier = Modifier.onClick {
setCount(count + 1)
}
Column {
BasicText("Count outside: $count", clickToIncCountModifier)
NavHost(navController, "0") {
repeat(4) { i ->
composable(i.toString()) {
Column {
BasicText("Screen $i")
BasicText("Count in `NavHost`: $count", clickToIncCountModifier)
}
}
}
}
}
Run the app on Wasm, and click the "count" texts to increment the text. If you click the text outside the NavHost, the value is correctly incremented; if you click the text inside, the value is always reset to 1 which is the original value 0 incremented. However, If I use MutableState.value directly in the lambda or use delegated properties for the state instead, it works. Also both texts increment and show the count correctly on JVM desktop.
After going through a bunch of Jetpack Compose docs and experimenting with the code myself, I suspect that this is a bug with the Compose compiler and the navigation library combined, firstly because it works on JVM desktop but not on JS and Wasm, and secondly because I see that lambdas are stable, so they are immutable and essentially closures wrapping val count and their recomposition is skipped, and while the compiler may have adopted some mechanism similar to rememberUpdatedState on desktop, it doesn't do it on JS or Wasm. However, LazyColumn has a similar style of adding composable lambdas in a non-composable lambda, but doesn't have this bug as I have tested. Nevertheless, These are merely my thoughts and of course you guys are the experts, so correct me if I am wrong.
I came across this problem when I was trying to port a small portion of Compose navigation to Compose HTML. And I spent quite some time debugging this. If you are interested in more details, see the commit messages of the latest 4 commits here.
Navigation Multiplatform version (for related issues): 2.7.0-alpha07
Kotlin version: 2.0.0
OS version(s) (required for Desktop and iOS issues): Ubuntu 22.04.4 LTS
OS architecture (x86 or arm64): x86
JDK (for desktop issues): OpenJDK Runtime Environment (build 21.0.3+9-Ubuntu-1ubuntu122.04.1) for Gradle, some JDK 8 for runtime because I set jvmToolchain(8)
Describe the bug
See the code below:
Run the app on Wasm, and click the "count" texts to increment the text. If you click the text outside the
NavHost
, the value is correctly incremented; if you click the text inside, the value is always reset to 1 which is the original value 0 incremented. However, If I useMutableState.value
directly in the lambda or use delegated properties for the state instead, it works. Also both texts increment and show the count correctly on JVM desktop.After going through a bunch of Jetpack Compose docs and experimenting with the code myself, I suspect that this is a bug with the Compose compiler and the navigation library combined, firstly because it works on JVM desktop but not on JS and Wasm, and secondly because I see that lambdas are stable, so they are immutable and essentially closures wrapping
val count
and their recomposition is skipped, and while the compiler may have adopted some mechanism similar torememberUpdatedState
on desktop, it doesn't do it on JS or Wasm. However,LazyColumn
has a similar style of adding composable lambdas in a non-composable lambda, but doesn't have this bug as I have tested. Nevertheless, These are merely my thoughts and of course you guys are the experts, so correct me if I am wrong.I came across this problem when I was trying to port a small portion of Compose navigation to Compose HTML. And I spent quite some time debugging this. If you are interested in more details, see the commit messages of the latest 4 commits here.
Affected platforms
NavHost
is not available on this, I copied and adapted aNavHost
myself and it still occurs. See https://github.com/huanshankeji/compose-multiplatform-material/commit/3c48921f8b89fd21c5f3227c463b62458c7df567 for details.)Versions
jvmToolchain(8)
To Reproduce
See the code and instructions above.