what is DisposableEffect and under the hood in jetpack compose?

3.7k Views Asked by At

I've been trying to understand what is DisposableEffect and How it works for a while so I have searched on the internet for this and I saw that most of the documentation are similar explanation for example

DisposableEffect is a powerful tool provided by Jetpack Compose that allows you to perform side effects in your composable functions that need to be cleaned up when the composable leaves the composition. You can use keys to control when the callback function is called.

I know that DisposableEffect works asynchronously like LaunchedEffect and based on key values but when onDispose method works as you can see the definition of above

that need to be cleaned up when the composable leaves the composition

This sentence probably defines the onDispose method, so in this case, it means onDispose will work, but what does it mean when the composable leaves the composition is written like this everywhere? I couldn't understand.

I did it like this for example

State

data class State(
    ...
    val isError:Int?=null,
    ...
)

UI

val errMsg = stringResource(id = R.string.error)
val savedMsg = stringResource(id = R.string.saved)

DisposableEffect(state.isError) {
        when (state.isError) {
            0 -> Toast.makeText(context, savedMsg, Toast.LENGTH_LONG).show()
            1 -> Toast.makeText(context, errMsg, Toast.LENGTH_LONG).show()
            else -> {}
        }
        onDispose {
            setIsError()
        }
    }

VM

  fun setIsError(){
    _state.update {
        it.copy(
            isError = null,
        )
    }
}
 

So when does on Dispose run for example ?

That's why I used onDisposableEffect here every time the user presses a button, if the operation is successful when the button is pressed, isError 0 comes, if not 1, I want to get a Success message in every successful operation, but once the button is pressed and the operation is successful, the Success message does not work because the key value is isError is still same 0, so I used DisposableEffect to achieve that problem but as I said I don't know the detailed and I did not fully understand

2

There are 2 best solutions below

3
Jorn On

Consider this example code:

@Composable
fun example() {
  var inComposition by remember { mutableStateOf(true) }
  if (inComposition) {
    DisposableEffect {
      ...
    }
  }
}

The DisposableEffect will leave the composition, and thus run its onDispose function, when the inComposition variable goes from true to false.

0
Thracian On

DisposableEffect is a remember function as LaunchedEffect difference being it observes remember states while latter comes with a coroutineScope.

dispose function gets called when the block/Composable DisposableEffect leaves composition due to RememberObserver's onForgotten function as below.

This implementation is similar to LaunchedEffect to work only once

@Composable
@NonRestartableComposable
fun DisposableEffect(
    key1: Any?,
    effect: DisposableEffectScope.() -> DisposableEffectResult
) {
    remember(key1) { DisposableEffectImpl(effect) }
}

LaunchedEffect

@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
    key1: Any?,
    block: suspend CoroutineScope.() -> Unit
) {
    val applyContext = currentComposer.applyCoroutineContext
    remember(key1) { LaunchedEffectImpl(applyContext, block) }
}

The section how dispose works is

private val InternalDisposableEffectScope = DisposableEffectScope()

private class DisposableEffectImpl(
    private val effect: DisposableEffectScope.() -> DisposableEffectResult
) : RememberObserver {
    private var onDispose: DisposableEffectResult? = null

    override fun onRemembered() {
        onDispose = InternalDisposableEffectScope.effect()
    }

    override fun onForgotten() {
        onDispose?.dispose()
        onDispose = null
    }

    override fun onAbandoned() {
        // Nothing to do as [onRemembered] was not called.
    }
}

This is basically how lifecycle of remember observation is done.

When you move to a next page, get out of a conditional block or scroll an item out of viewport of LazyColumn/Row onDispose can be used to observe when your block leaves composition.

Does lazyColumn listen for events when items enter or leave the screen