https://kotlinlang.org logo
#compose
Title
# compose
s

Sam

03/01/2021, 10:07 PM
Does anyone have clear guidance for
onActive
and
onDispose
? We went from clear semantic names to
DisposableEffect
and i have no idea what to pass in as a parameter..
j

Jordi Saumell

03/01/2021, 10:11 PM
This might help you
s

Sam

03/01/2021, 10:15 PM
But doesn’t choosing a state to pass in get away from the spirit of
onActive
and
onDispose
? I just want generic component lifecycle events, now I have to think about which piece of state is an argument when that isn’t what I care about. Am I missing something?
j

jossiwolf

03/01/2021, 10:47 PM
Why do you care about lifecycle events?
s

Sam

03/01/2021, 11:48 PM
I don’t understand the question @jossiwolf? Lifecycle events are extremely important in component development, that’s why SwiftUI includes
onAppear/onDisappear
React includes
componentDidMount/componentWillUnmount
and Jetpack Compose presumably included
onActive/onDispose
. We use them all the time, just like every other developer building production apps.
👍 1
a

Albert Chang

03/02/2021, 12:00 AM
You can just pass
Unit
to
DisposableEffect
.
1
c

Colton Idle

03/02/2021, 12:15 AM
I could have sworn I read something from @Adam Powell about not needing lifecycle events in composables? I could be wrong. In that case, my apologies Adam.
s

Sam

03/02/2021, 12:27 AM
Thanks @Albert Chang! This allows us to migrate our code to
beta01
in a straightforward manner 🙏
a

Adam Powell

03/02/2021, 12:32 AM
fwiw passing
Unit
or
true
or any other constant as effect key parameters is intended to look suspicious. It has its uses, but it correlates strongly with building composables that are more stateful than they could be, and that might break if something in their lambda capture changes during recomposition and the effect holds a stale reference to the original value instead of updating.
s

Sam

03/02/2021, 12:36 AM
Hmm, that confuses me and maybe I should ask a different way:
In react we have
componentDidMount/componentWillUnmount
and SwiftUI
onAppear/onDisappear
and we used to have
onActive/onDispose
which we used quite frequently when components loaded and unloaded (subscribe to stuff, scroll to initial position, etc). Now that those are gone, what is the recommended way to use these lifecycle events without writing suspicious code?
c

Colton Idle

03/02/2021, 12:52 AM
I don't use react and I'm not a compose expert, but wouldn't you just have state passed into the composable and if it updates then the composable would update as well. Reacts componentDidMount sounds side-effecty.
s

Sam

03/02/2021, 12:59 AM
@Colton Idle for the majority of scenarios we try and write our components to be as stateless as possible, which is inline with the approach you are suggesting. That being said, there are also plenty of cases where we need to have code that runs when the component is loaded, and more code that runs when unloaded. To quantify,
onAppear
is used 92 times in our
SwiftUI
codebase of 150K lines.
e

eygraber

03/02/2021, 3:51 AM
I think the point is that those things (
onAppear/onDisappear
etc...) can be modeled as state if they need to be used for UI, and that would be the "correct" way to use Compose. If you need those callbacks to have an effect that's not related to UI (i.e. a side effect) you can use
DisposableEffect
or one of the other types of effects. I think what @Adam Powell was referencing about looking suspicious is that the API is purposely clunky for that use case so that you would reconsider if you actually need to use it that way. I personally try to keep my compose layer as "dumb" as possible so that the only thing it knows about is rendering state. If I needed to do something non UI related in response to lifecycle I would do it from the Activity/Fragment or a lifecycle observer, etc...
1
s

Sam

03/02/2021, 4:59 AM
In our experience, lifecycle events are important when you need super precise control over data loading / animation / scroll position / performance scenarios. Especially in an app like ours, where everything is in realtime and you can have 25K simultaneous users all in one chatroom. Coming from React and SwiftUI, the decision to obfuscate lifecycle hooks here seems unintuitive.
a

Adam Powell

03/02/2021, 3:24 PM
It's fine to use `*Effect`s for these purposes; we do it plenty ourselves too. The required arguments aren't to obfuscate, but because 9 times out of 10, you really do need to think about the data dependencies and end to end lifetime of the effect. We saw people write a lot of code like this with the old apis, even when they knew how the system worked:
Copy code
@Composable
fun MyComposable(
  state: MyState,
  params...
) {
  onActive {
    record(state)
  }
it started innocent enough; "it's just prototyping," "the state parameter never changes when I call it"
(we had long debates around whether to make determination of keys fully automatic from the lambda capture too, but real code has plenty of related cases where predicting the stability and equivalence of a lambda capture is an unreasonable thing to ask of people)
and as for the name change, it's significant that effects are their own entities in the composition with behavior, not just floating callback registrations. These two pieces of code have similar behavior:
Copy code
if (foo != null) {
  DisposableEffect(foo) {
    // ...
  }
}
and
Copy code
if (foo != null) {
  Button(onClick = { foo.performAction() }) {
    // ...
  }
}
s

Sam

03/02/2021, 8:14 PM
Thanks for providing more color on the decisions, @Adam Powell, and I can see how people new to components/stateless/unidirectional flow could make those mistakes. I also see how floating onActive callbacks are a bit too magical. However I do still feel that the pendulum has swung too far for those of us who use component lifecycle events responsibly. I’m considering creating custom
ComponentAppear { }
and
ComponentDisappear { }
in our codebase to make the code cleaner for these use cases, is this currently possible?
a

Adam Powell

03/02/2021, 8:15 PM
sure, go nuts.
DisposableEffect
and
LaunchedEffect
are both built on the public
RememberObserver
API
lmk if you have any questions working with it
👍 1
if you
remember {}
an object that implements that interface compose will give it callbacks at various points in the remember lifecycle. This is about the only/lowest level "lifecycle" Compose works in
do beware that if you let a reference to a
RememberObserver
escape to user code and that user code passes it to composable functions as a parameter or `remember {}`s it in other places, it can linger in the composition for longer than you might expect. Best practice is to not leak references to them to user code unless you're prepared to deal with this.
2 Views