https://kotlinlang.org logo
#compose
Title
# compose
h

Hitanshu Dhawan

10/25/2020, 2:03 AM
Hello everyone, I’m facing an issue where I’m updating my LiveData in ViewModel, but there is no recomposition happening after the LiveData is updated. Here, I’m changing the quantity of a menu item (
var
). If I remove an item, then there is recomposition happening. How to handle such cases in compose? ViewModel
Copy code
class MenuViewModel : ViewModel() {

    private val data = MenuRepository.getMenuData()

    private val _menuItems = MutableLiveData(data.menuItems)
    val menuItems: LiveData<List<MenuItem>> = _menuItems

    fun incrementMenuItem(menuItem: MenuItem) {
        _menuItems.value = _menuItems.value!!.apply { find { it == menuItem }!!.quantity++ }
    }

}
In Composable function
Copy code
val menuItems by viewModel.menuItems.observeAsState()

        LazyColumnFor(
            items = menuItems!!
        ) { menuItem ->
            MenuItem(
                menuItem = menuItem,
                onIncrement = { viewModel.incrementMenuItem(menuItem) },
                onDecrement = { viewModel.decrementMenuItem(menuItem) },
                modifier = Modifier.padding(16.dp)
            )
        }
m

mannodermaus

10/25/2020, 7:41 AM
Is MenuItem a data class? If not, does it provide an equals() implementation? Not sure if the order of operations might be troublesome here, esp. with the post-increment and apply. It’s more verbose, but could you try if it works with a more explicit order like this? Alternatively, maybe it would be better to remove the old object altogether and insert a copy with the updated quantity at its former position in the list.
h

Hitanshu Dhawan

10/25/2020, 10:18 AM
Yes, MenuItem is a data class, so it has correct equals() implementation. I tried updating the list with a newly created object and it worked.
Copy code
fun incrementMenuItem(menuItem: MenuItem) {
    val index = _menuItems.value!!.indexOf(menuItem)
    val newMenuItem = menuItem.copy()
    newMenuItem.quantity++
    
    _menuItems.value = _menuItems.value!!.toMutableList().also { it[index] = newMenuItem }
}
But, can’t we have a better solution for this? If the data is deeply nested then it will be super hard to make these new objects. Is there a better way for this?
Looks like Recomposition happens if any element of list is changed/added/removed but not if any element’s inner value changes. Is it possible to have recomposition if any element’s inner value changes?
g

Grigorii Yurkov

10/25/2020, 6:12 PM
I don't know will it help or not, but I would rather use
State
directly without
LiveData
h

Hitanshu Dhawan

10/25/2020, 6:41 PM
I even tried having State in VM instead of LiveData, but that also didn’t help. Even that would require to make a new copy of the list element.
g

Grigorii Yurkov

10/25/2020, 7:10 PM
Did you try
mutableStateListOf()
?
h

Hitanshu Dhawan

10/27/2020, 2:42 PM
Tried with
mutableStateListOf
as well, but that also did not work.
Copy code
var menuItemsState = mutableStateListOf<MenuItem>(*data.menuItems.toTypedArray())

menuItemsState = menuItemsState.also { it.find { it == menuItem }!!.quantity++ }