Difference between remember and rememberUpdatedState in Jetpack Compose?
Asked Answered
E

3

31

I'm confused, can someone explain me the difference between:

val variable by remember { mutableStateOf() }

and

val variable by rememberUpdatedState()

When I check the source code of rememberUpdatedStates I actually see: remember { mutableStateOf() }

@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
    mutableStateOf(newValue)
}.apply { value = newValue }
Earsplitting answered 7/9, 2021 at 8:45 Comment(1)
This article helps me to understand this better: proandroiddev.com/…Georginegeorglana
P
32

remember is needed when you don't want to do some heavy calculation/operation when your composable is recomposed. On the other hand, sometimes your operation might change so you need to do calculations or update remembered values to make sure not to use obsolete values from the initial calculation.

fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
    mutableStateOf(newValue)
}.apply { value = newValue }

rememberUpdatedState function is the same as using remember with mutableState to trigger recomposition when value changes.

@Composable
private fun Calculation(input: Int) {
    val rememberUpdatedStateInput by rememberUpdatedState(input)
    val rememberedInput = remember { input }

    Text("updatedInput: $rememberUpdatedStateInput, rememberedInput: $rememberedInput")
}

var myInput by remember {
    mutableStateOf(0)
}

OutlinedButton(
    onClick = {
        myInput++

    }
) {
    Text("Increase $myInput")
}
Calculation(input = myInput)

This is a very basic example to show how values from remember and rememberUpdatedState change.

A more practical example is with lambdas.

For example, suppose your app has a LandingScreen that disappears after some time. Even if LandingScreen is recomposed, the effect that waits for some time and notifies that the time passed shouldn't be restarted:

@Composable
fun LandingScreen(onTimeout: () -> Unit) {

    // This will always refer to the latest onTimeout function that
    // LandingScreen was recomposed with
    val currentOnTimeout by rememberUpdatedState(onTimeout)

    // Create an effect that matches the lifecycle of LandingScreen.
    // If LandingScreen recomposes, the delay shouldn't start again.
    LaunchedEffect(true) {
        delay(SplashWaitTimeMillis)
        currentOnTimeout()
    }

    /* Landing screen content */
}

In this example LaunchedEffect is invoked once but this LandingScreen function can be recomposed and might require you to change onTimeOut, so using rememberUpdatedState makes sure that the latest onTimeout is called after delay.

Pull answered 4/12, 2021 at 5:35 Comment(0)
O
24

The difference between remember and rememberUpdatedStates are:

remember

Remember the value produced by calculation. calculation will only be evaluated during the composition. Recomposition will always return the value produced by composition.

When you use remember, every consecutive calls to recomposition will only return same value that was computed initially during first call to remember. You can consider this as an read-only state that you can not update on future reference while recomputing will reference to initial evaluation.


rememberUpdatedStates

remember a mutableStateOf and update its value to newValue on each recomposition of the rememberUpdatedState call.

rememberUpdatedState should be used when parameters or values computed during composition are referenced by a long-lived lambda or object expression. Recomposition will update the resulting State without recreating the long-lived lambda or object, allowing that object to persist without cancelling and resubscribing, or relaunching a long-lived operation that may be expensive or prohibitive to recreate and restart.

Here, it is expected that sometimes your calculation can take a while and computation may be considerable slow. In such cases, you're provided with latest value rather than lambda that will take impact on every recomposition so that you can have reference to the latest value produced by calculation.

By using this method, you make sure that your UI is updated by every recomposition without recreating long-lived lambdas or relaunching long-lived operations that you may have during remember method's lambda callbacks.

Obaza answered 7/9, 2021 at 9:5 Comment(3)
A great explanation but now I have a question on the difference between regular val and rememberUpdatedStates?Rowe
@ArpitPatel I know I shouldn’t put it this way but just for your understanding, you can consider that regular "remember" is immutable state holder (something like val) while "rememberUpdatedStates" is a mutable one (something like var) which provides consecutive changes after first load if needed.Obaza
@ArpitPatel If you just need to use the value directly in your composable and you don't need to reference it in a callback or lambda (e.g: a LaunchedEffect) that might be executed outside the composition scope, you can just use a regular val. But if you need to ensure that a callback or lambda always has the most recent state, even when it is invoked outside of the composition, use rememberUpdatedState.Vocabulary
S
4

Jeel gave a great answer so I will just add some sample code.

I've created an example that demonstrates the effect of rememberUpdatedState() on LaunchedEffect with and without it. The example is very simple:

  1. the user schedules a message that will be passed inside a lambda to LaunchedEffect.

  2. After a while the user creates another message that will be passed inside a lambda to the exact same LaunchedEffect as the previous message.

  3. In case the user chooses to use rememberUpdatedState() - the LaunchedEffect will get updated with the new lambda. In case they don't - LaunchedEffect will run with the initial lambda and will ignore further recompositions.

Here is the code that performs the "decision making" and ultimately either updates LaunchedEffect or doesn't:

@Composable
fun ShowToast(
    useRememberUpdatedState: Boolean,
    message: (() -> String)? = null
) {
    val context = LocalContext.current
    var actualTrueMessage: State<(() -> String)?>? = null
    if (useRememberUpdatedState) {
        actualTrueMessage = rememberUpdatedState(message)
    }
    LaunchedEffect(Unit) {
        delay(MESSAGE_DELAY)
        Toast.makeText(
            context,
            actualTrueMessage?.value?.invoke() ?: message?.invoke(),
            Toast.LENGTH_SHORT
        ).show()
    }
}

Please note that in my example I'm specifically launching LaunchedEffect with a Unit key so that it is launched exactly once for composition and recompositions.

Here is what it looks like:

LaunchedEffect with and without the usage of rememberUpdatedState()

Sheerlegs answered 20/7, 2023 at 16:19 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.