Hello I have a singleton object which has a "Curr...
# compose
m
Hello I have a singleton object which has a "Current Customer" field on it and I would like to trigger a view refresh when this customer object changes. Currently doing...
Copy code
val customerState: MutableState<CustomerData?> = remember { mutableStateOf(Manager.getInstance().customer) }

    key(customerState.value?.id) {
// all the compose stuff
}
The manager class is in java if that makes any difference... Is this incorrect way to use
key()
? It appears to only work on the initial composition when the fragment is created and I've verified that observed value is being updated. But the updates to the customer field are not triggering the block inside
key()
z
why do you want to force recomposition when the ID changes?
m
Because the customer changes and I need the view to render the new customer info
z
Right, but you shouldn’t need to use
key
at all for that
Anything that is reading or loading data based on the
id
should automatically refresh without
key
m
Well I think we can ignore the ID here, That was just me trying something out but I think I get what you're saying and I'll give it a shot by removing key
z
Oh wait ok i see the problem.
Manager
is your singleton, and you’re only reading
customer
inside the
remember
.
m
Yes, correct
z
Based on this very limited code snippet, i’m not sure why
customerState
needs to be a `MutableState`—are you ever updating that property?
Is
Manager.customer
backed by a
MutableState
?
m
No, it's not being updated in the composable but the customer field on the singleton is updated all over the place and no matter where it's updated I want to capture that.
The manager class is a legacy Java class so it's not backed by immutable state. I'm not sure if that's supported
z
So making
customerState
a
MutableState
won’t magically make that
customer
observable or anything. It means you can now manually set it to a new
CustomerData
value, and that change will be observable. But you’d need to wire that up manually.
You should be able to call
mutableStateOf
from java, i believe, but if this is in a large codebase legacy module, you might not want to add the compose dependency, idk
How does your legacy code know when
Manager.customer
property changes?
m
We've already added the compose dependencies, new development is in compose and trying to move old stuff to more modern code one piece at a time. Right now there are just callbacks, the fragment invokes
customerChanged()
which given the old architecture, we could invoke just some
refresh()
type method and update the xml.
z
I imagine you could probably do something like this:
Copy code
@Composable fun Customer(manager: Manager)
  var customerState: CustomerState? by remember(manager) { mutableStateOf(manager.customer) }
  DisposableEffect(manager) {
    val listener = fun(newCustomer: CustomerData?) { customerState = it }
    manager.addCustomerChangedListener(listener)
    onDispose {
      manager.removeCustomerChangedListener(listener)
    }
  }

  // …rest of customer view
}
m
🤔 ahh so for my understanding....this still requires
key()
?
DisposableEffect
simple defines a side effect for each new unique value of `key`(argument not function). Which in this case is only 1 value, the singleton. And the side-effect is setting the listener and defining the behavior for `onDispose`; removing the listener. Inside the side-effect we're able to update that mutable state when the listener is triggered and as a result
key()
is able to observe the changes to the mutableState. This is super helpful!
z
You don’t need the
key()
function for this at all
I pass
manager
as keys to
remember
and
DisposableEffect
for correctness – in production, the value of that parameter would never change if it’s a singleton, so their lifetimes would match that of the composable.
DisposableEffect
simple defines a side effect for each new unique value of `key`(argument not function). Which in this case is only 1 value, the singleton. And the side-effect is setting the listener and defining the behavior for `onDispose`; removing the listener.
Yes
Inside the side-effect we’re able to update that mutable state when the listener is triggered
Yes
as a result
key()
is able to observe the changes to the mutableState
Yes, although there’s nothing special about the
key()
function, and anything in the composable that reads the
customerState
value should automatically update when it changes.
E.g. you could have something like this:
Copy code
@Composable fun Customer(manager: Manager)
  var customerState: CustomerState? by remember(manager) { mutableStateOf(manager.customer) }
  DisposableEffect(manager) {
    val listener = fun(newCustomer: CustomerData?) { customerState = it }
    manager.addCustomerChangedListener(listener)
    onDispose {
      manager.removeCustomerChangedListener(listener)
    }
  }

  Column {
    CustomerName(customerState.name)
    CustomerAddress(customerState.address)
    CustomerPaymentInfo(customerState.paymentInfo)
    …
  }
}
and those composables would get the new details whenever the custom state changes
m
Yes, although there’s nothing special about the
key()
function, and anything in the composable that reads the
customerState
value should automatically update when it changes.
I see, simply using remember & mutableState enables us to update that remembered value and observe the updated value. 👍 Would a more common use of
key()
be for example.... If I had some sub-composables inside this larger
Customer(manager: Manager
and I want to have finer control over that execution block even though the outer is undergoing re/composition?
z
Kind of …
It lets you force restart the lifetime of an entire subtree of a composition. I can’t think of a “common” example of it because it shouldn’t be that common to use
thank you color 1
👌 1