Skip to content

Instantly share code, notes, and snippets.

@vRallev
Last active March 10, 2025 22:48
Show Gist options
  • Select an option

  • Save vRallev/7885592b09dce14d891cd85089475130 to your computer and use it in GitHub Desktop.

Select an option

Save vRallev/7885592b09dce14d891cd85089475130 to your computer and use it in GitHub Desktop.
/**
* Used to process events returned by the UI layer in the scope that runs our presenters, which
* usually is `PresenterCoroutineScope`. Without this wrapper events would be processed on the
* main thread and could potentially block the UI or cause lag.
*
* Furthermore, the returned lambda is remembered as state within the composable, which allows
* to make [BaseModel] implementations data classes. Without remembering every [BaseModel] instance
* would not equal another instance, since different lambda instances are never equal to each
* other.
*
* A common pattern looks as follows:
* ```
* @Inject
* class LoginPresenter(..) : MoleculePresenter<Unit, Model> {
*
* @Composable
* override fun present(input: Unit): Model {
* // ...
* return Model(
* name = ..
* onEvent = onEvent {
* when (it) {
* is Event.OnNameClick -> ...
* is Event.Login -> ...
* }
* }
* )
* }
*
* data class Model(
* val name: String,
* val onEvent: (Event) -> Unit
* ) : BaseModel
*
* sealed interface Event {
* object OnNameClick : Event
* class Login(val userName: String) : Event
* }
* }
* ```
*/
@Suppress("unused", "UnusedReceiverParameter")
@Composable
fun <EventT : Any> MoleculePresenter<*, *>.onEvent(
handler: @DisallowComposableCalls suspend (EventT) -> Unit,
): (EventT) -> Unit {
// This function creates, remembers and returns a separate lambda from the the `handler`
// argument. The newly created lambda forwards events to the last `handler` lambda this
// function was called with.
val scope = rememberCoroutineScope()
val lambdaReference = remember { AtomicRefWrapper(handler) }
lambdaReference.value = handler
DisposableEffect(Unit) {
onDispose {
// Release the lambda
lambdaReference.value = null
}
}
return remember {
{ event ->
// Launch a coroutine in this scope so that we process the event on the
// PresenterCoroutineScope.
scope.launch {
lambdaReference.value?.invoke(event)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment