Home > Back-end >  Jetpack Compose - Why remembered custom object not holding changes after recomposition
Jetpack Compose - Why remembered custom object not holding changes after recomposition

Time:06-22

I have a custom object with a boolean value that I want to observe on my screen to change my layout.

Example:

data class Book(isFavorited: Boolean)


@Composable 
fun ShowBook(book: Book, onConfirm: (Book) -> Unit){
   val bookState = remember { mutableStateOf(book) }

   //just an example
   val color = if(bookState.value.isFavorited) Color.Red else Color.White

   //changing the value of isFavorited, should do recomposition?
   Button(onClick { bookState.value.isFavorited = true }){
     Text("add to fav")
   }

   Button(onClick { bookState.value.isFavorited = false }){
     Text("remove from fav")
   }

   Button(onClick { onConfirm.invoke(bookState.value) }){
     Text("Confirm")
   }       
 }

If I press the "add to fav" button and set to my bookState a true value for isFavorited, and after that I press "Confirm", the value that I'll receive is still false (default value).

Does Anyone know what's happening?

Because I through that changing the bookState attributions it should recompose and set the correct value to the object.

CodePudding user response:

I did a couple of improvements in your code... It should work now.

In order to perform a recomposition, you must have to create a new instance of the object. In the code below this is done by calling the copy function just changing the params that you want (in case the class has more fields).

data class Book(val isFavorited: Boolean)

@Composable
fun ShowBook(book: Book, onConfirm: (Book) -> Unit) {
    // using "by" to avoid use the ".value" everywhere
    var bookState by remember { mutableStateOf(book) }

    // just an example
    val color = if (bookState.isFavorited) Color.Red else Color.White

    // changing the value of isFavorited, should do recomposition?
    Button(onClick = { bookState = bookState.copy(isFavorited = true) }){
        Text("add to fav")
    }

    Button(onClick = { bookState = bookState.copy(isFavorited = false) }){
        Text("remove from fav")
    }
    // You don't need the "invoke" here. 
    // It can be useful if the lambda is nullable:
    // onConfirm?.invoke(bookState)
    Button(onClick = { onConfirm(bookState) }){
        Text("Confirm")
    }
}

CodePudding user response:

What's wrong with your code is you remember the book on first composition and it never gets updated because remember{} stores values on first composition, on next compositions or recompositions even if your book parameter changes remember block is never executed. You can verify this by logging

 val bookState = remember {
    println("Remembered value: $book")
    mutableStateOf(book) 
}

that's why there is another method called rememberUpdatedState

val bookState = rememberUpdatedState(book)

and if you check source code you will see that

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

it just updates value by changing mutableState.value with newValue

or you can manually call bookState.value = book after remember.

   val bookState = remember { mutableStateOf(book) }
   bookState.value = book
  • Related