SideEffect and produceState

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:

Recompose.kt: the composition counter used in Section 5.14Kotlin
@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:

EffectDemos.kt: loading a book into StateKotlin
  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:

Output of 95
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).