SideEffect { } runs after every applied composition, so it can push state into objects Compose 234 does not manage, such as the counter behind State and Recomposition's logs:
@Composable
inline fun LogCompositions(name: String) { // inline: runs as part of its caller
val counter = remember { CompositionCounter() } // kept across recompositions, not State
SideEffect { // after every applied (re)composition
counter.count++
Log.d(TAG, "$name composed #${counter.count}")
}
}It is inline so it is never skipped, and the counter is not State, which would recompose again. produceState(initial, key) { } works the other way: a coroutine sets the returned State's value:
val book by produceState<Book?>(initialValue = null, id) { // restarts when id changes
Log.d(FX, "produceState: loading $id")
value = null // the State itself is not reset for you
delay(800)
value = books.first { it.id == id }
Log.d(FX, "produceState: value set for $id")
awaitDispose { Log.d(FX, "produceState: disposed for $id") }
}Next book changes id from 1 to 2:
D/BookNestFx: produceState: loading 1 D/BookNestFx: produceState: value set for 1 D/BookNestFx: produceState: disposed for 2 D/BookNestFx: produceState: loading 2 D/BookNestFx: produceState: value set for 2
awaitDispose printed 2, not 1: it reads the id state when it runs, so copy a key into a local val if cleanup needs the old one. For Flows, use collectAsStateWithLifecycle() (ViewModel and UDF).