Mokera

Tech Sheets

mokelab

How to use rememberUpdatedState

Last updated:2024-07-10

You may want to execute with arguments of Composable after performing time-consuming processing on LaunchedEffect or DisposableEffect .

 @Composable
internal fun MyComposable(
    done: () -> Unit,
) {

    LaunchedEffect(Unit) {
        // wait 3 seconds
        delay(3000)
        // call done given at Composable argument.
        done()
    }

    Text("...")
}

We don't want to restart the coroutine every time done() changes, so we specify Unit as the argument to LaunchedEffect() .

However, if re-compose is happened, done() may change. In such cases, you can use rememberUpdatedState .

 @Composable
internal fun MyComposable(
    done: () -> Unit,
) {

    val currentDone by rememberUpdatedState(done)

    LaunchedEffect(Unit) {
        // wait 3 seconds
        delay(3000)
        // call the value of rememberUpdatedState
        currentDone()
    }

    Text("...")
}

Now, after doing some time-consuming processing, done() that is replaced with the latest will be called.

Example

As It's hard to understand by Official document,let's look at a specific example.

@Composable
fun MainScreen() {
    var counter by remember { mutableStateOf(0) }
    // for result
    var goodDone by remember { mutableStateOf(-1) }
    var badDone by remember { mutableStateOf(-1) }
    // capture current value
    val v = counter

    Column(modifier = Modifier.padding(paddingValues)) {
        Bad(
            count = counter,
            doneValue = badDone,
        ) {
            badDone = v
        }

        Good(
            count = counter,
            doneValue = goodDone,
        ) {
            goodDone = v
        }
        
        // button to increment counter
        Button(onClick = {
            ++counter
        }) {
            Text("Add")
        }
    }
}

For Bad , we will call the argument done() directly.

@Composable
fun Bad(
    count: Int,
    doneValue: Int,
    done: () -> Unit,
) {

    LaunchedEffect(Unit) {
        delay(3000)
        // call directly
        done()
    }

    Text("Count=${count} done=${doneValue}")
}

For Good , we will call the value of rememberUpdatedState .

@Composable
fun Good(
    count: Int,
    doneValue: Int,
    done: () -> Unit,
) {
    val currentDone by rememberUpdatedState(done)

    LaunchedEffect(Unit) {
        delay(3000)
        // call the value of rememberUpdatedState
        currentDone()
    }

    Text("Count=${count} done=${doneValue}")
}

Run it and press the button repeatedly within 3 seconds.

For Bad , done() is from the first composition so the doneValue became 0. For Good , done() is from the last time composition so the doneValue became 9.

Back