Lucas Sousa
Lucas Sousa

Reputation: 352

What is the best way to persist state in composable recreation in Jetpack Compose?

Consider we have the following structure in a toggling implementation scenario:

@Composable
fun RootComposable() {
  var someAuxToggle by remember { mutableStateOf(false) }

  if (someAuxToggle) {
    FirstComposable { someAuxToggle = !someAuxToggle }
  } else {
    SecondComposable { someAuxToggle = !someAuxToggle }
  }
}

@Composable
fun FirstComposable(auxCallback: () -> Unit) {
  val myRandomNumber by remember { mutableStateOf(Random.nextInt(100)) }
  Button(onClick = { auxCallback() }) {
    Text(text = "I'm the FirstComposable.\nMy random is: $myRandomNumber")
  }
}

@Composable
fun SecondComposable(auxCallback: () -> Unit) {
  val myRandomNumber by remember { mutableStateOf(Random.nextInt(100)) }
  Button(onClick = { auxCallback() }) {
    Text(text = "I'm the SecondComposable.\nMy random is: $myRandomNumber")
  }
}

Doing this, the toggling works, the composables are correclty called, however their own state changes, once I'm "recreating" then. This is also valid if those buttons was placed inside other top composables and these top composables be called.

Now, if we implement this in a little hacky way the state keeps alive (the random numbers are not regenerated):

@Composable
fun RootComposable() {
  var someAuxToggle by remember { mutableStateOf(false) }

  Box(
    if (someAuxToggle)
      Modifier.wrapContentSize()
    else
      Modifier.size(0.dp)
  ) {
    FirstComposable { someAuxToggle = !someAuxToggle }
  }

  Box(
    if (someAuxToggle)
      Modifier.size(0.dp)
    else
      Modifier.wrapContentSize()
  ) {
    SecondComposable { someAuxToggle = !someAuxToggle }
  }
}

@Composable
fun FirstComposable(auxCallback: () -> Unit) {
  val myRandomNumber by remember { mutableStateOf(Random.nextInt(100)) }
  Button(onClick = { auxCallback() }) {
    Text(text = "I'm the FirstComposable.\nMy random is: $myRandomNumber")
  }
}

@Composable
fun SecondComposable(auxCallback: () -> Unit) {
  val myRandomNumber by remember { mutableStateOf(Random.nextInt(100)) }
  Button(onClick = { auxCallback() }) {
    Text(text = "I'm the SecondComposable.\nMy random is: $myRandomNumber")
  }
}

Note that in this implementation both buttons are being called inside root but the toggling was made by switching their "visibility", via setting their sizes.

Also note that the buttons composable are really the same component, but my idea is just demonstrate the state behaviour.

My question is about the best way to keep the state if we call a composable again, like in the first code; if there are a simple way to do it or if we really need to store those states in some top level fields or something like this.

Also, is the second approach too expensive in terms of performance? I think yes, because both composable are "turned on" all the time, then if we have composables that makes hard work (e.g., via side effects or even background tasks) then performance can be lowered.

Anyway, I hope we find a solution to that.

Upvotes: 1

Views: 1175

Answers (1)

z.g.y
z.g.y

Reputation: 6197

It depends on the scope of how you want to recreate or restore a state, you can either hoist it in a viewmodel where a state is dependent on its lifecycle, or inside a rememberSaveable where you have to define a Saver for it to save and restore the state. So far, I only consider using rememberSaveable when I only want to survive and restore states during configuration changes (i.e light to dark, screen rotate), on the other hand, I hoist states via ViewModel when functionalities of a composable depends on much deeper functionalities (i.e repository/network calls, heavy business use-cases)

Also Both of the implementation will trigger the entire re-composition of RootComposable as it observes a mutableState

 if (someAuxToggle) 
 ...
 ...

I'm not quite sure though if the second one has a significant performance hit assuming this is only the scope of your actual code.

I'd recommend watching this once in a while as a refresher when making hoisting decisions A Compose state of mind: Using Jetpack Compose's automatic state observation

Upvotes: 2

Related Questions