-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix new presenter instances of the same type not being recomposed #799
Conversation
This is an interesting edge case I ran into and subsequently learned about after talking with other folks. In short, because compose treats these by type and source position, you can think of the following two snippets as equivalent. ```kotlin class Foo(val value: String) { @composable fun Content() { val myState = remember { init(value) } } } ``` ```kotlin @composable fun Content(self: Foo) { val myState = remember { init(self.value) } } ``` This affects Circuit presenters disproportionately because their present() calls have no other inputs to disambiguate. As a result, compose skips this in cases where you have a possibly different screen but same presenter _type_, and we need to explicitly force recomposition with a `key()`. A good example of this issue can be seen like so ```kotlin var count by remember { mutableIntStateOf(0) } val screen = CountScreen(id) Column { Button(onClick = { id++ }) { Text("Increment count") } CircuitContent(screen) } ``` Where `ExampleScreen` just displays the current count. Before this change, this would never recompose the state.
Depends on #800 |
|
||
// Regression test for https://github.com/slackhq/circuit/pull/799 | ||
@Test | ||
fun contentShouldRecomposeStateWhenPresenterInstanceChanges() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed this fails with the previous behavior and passes now 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. It makes sense to me - why would Compose rerun the zero argument present()
method? From it's perspective, nothing has changed
|
||
// Regression test for https://github.com/slackhq/circuit/pull/799 | ||
@Test | ||
fun contentShouldRecomposeStateWhenPresenterInstanceChanges() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
The surprising thing is that it's not exactly zero argument, the enclosing class becomes an implicit receiver parameter and in theory that has changed, but compose doesn't consider it that way unless the type is different. |
The current implementation uses the presenter itself as the key(), which does fix the original issue. For our usage though, the presenter will always be a new instance, and therefore there will never be a consistent key across back stack changes (i.e. a push, then pop). This then forces the currentCompositeKeyHash for the underlying content underneath to change, which then breaks rememberRetained() when used without an explicit key. Fixed by changing the key to the `screen` instance instead, which achieves the same original fix, but the screen is consistent across navigation changes, which allows the currentCompositeKeyHash to be consistent.
The current implementation uses the presenter itself as the key(), which does fix the original issue. For our usage though, the presenter will always be a new instance, and therefore there will never be a consistent key across back stack changes (i.e. a push, then pop). This then forces the currentCompositeKeyHash for the underlying content underneath to change, which then breaks rememberRetained() when used without an explicit key. Fixed by changing the key to the `screen` instance instead, which achieves the same original fix, but the screen is consistent across navigation changes, which allows the currentCompositeKeyHash to be consistent.
This is an interesting edge case I ran into and subsequently learned about after talking with other folks. In short, because compose treats these by type and source position, you can think of the following two snippets as equivalent.
This affects Circuit presenters disproportionately because their present() calls have no other inputs to disambiguate. As a result, compose skips this in cases where you have a possibly different screen but same presenter type, and we need to explicitly force recomposition with a
key()
.A good example of this issue can be seen like so
Where
ExampleScreen
just displays the current count. Before this change, this would never recompose the state.