slackhq/circuit

Nested `RetainedStateRegistry` is saved even if `LocalCanRetainChecker` is `false`

alexvanyo opened this issue · 1 comments

In rememberRetained, the special case for saving a RetainedStateRegistry doesn't take into account the current canRetainChecker value before saving.

As a result, even if canRetainChecker returns false, the RetainedStateRegistry will save all children and then save itself into the parent.

After saving, the entry for the RetainedStateRegistry then unregisters itself:

fun saveIfRetainable() {
// If the value is a RetainedStateRegistry, we need to take care to retain it.
// First we tell it to saveAll, to retain it's values. Then we need to tell the host
// registry to retain the child registry.
val v = value
if (v is RetainedStateRegistry) {
v.saveAll()
registry?.saveValue(key)
}
if (registry != null && !canRetainChecker.canRetain(registry!!)) {
entry?.unregister()
// If value is a RememberObserver, we notify that it has been forgotten
if (v is RememberObserver) v.onForgotten()
}
}

This causes an issue with the NavigableCircuitContent use case where a CanRetainChecker is used to try to prevent retained state from being saved for a record that is no longer on the backstack:

CompositionLocalProvider(LocalCanRetainChecker provides recordInBackStackRetainChecker) {

The record-specific recordRetainedStateRegistry for a record that has been removed from the backstack will save itself anyway when it leaves composition, which means that I don't think the recordInBackStackRetainChecker currently has an effect as intended.

Note: investigated from integrating circuit-retained into my own navigation setup in alexvanyo/composelife#1599 where I ran into the same issue.

I tried taking the canRetainChecker into account in #1253, but it looks like changing this behavior conflicts with #1173.

The current multi-backstack setup is relying on this behavior: when the backstack changes, it's desired behavior to keep the state retained for the other backstack, but if the record isn't in the current backstack, then recordInBackStackRetainChecker is false, and therefore making the changes in #1253 loses retained state for the non-active backstacks.