Compose: LazyColumn recomposes all items on single item update

Viewed 1406

I am trying to show a list of Orders in a list using LazyColumn. Here is the code:

@Composable
private fun MyOrders(
    orders: List<Order>?,
    onClick: (String, OrderStatus) -> Unit
) {
    orders?.let {
        LazyColumn {
            items(
                items = it,
                key = { it.id }
            ) {
                OrderDetails(it, onClick)
            }
        }
    }
}

@Composable
private fun OrderDetails(
    order: Order,
    onClick: (String, OrderStatus) -> Unit
) {
    println("Composing Order Item")
    // Item Code Here
}

Here is the way, I call the composable:

orderVm.fetchOrders()
val state by orderVm.state.collectAsState(OrderState.Empty)

if (state.orders.isNotEmpty()) {
    MyOrders(state.orders) {
        // Handle status change click listener
    }
}

I fetch all my orders and show in the LazyColumn. However, when a single order is updated, the entire LazyColumn gets rrecomposed. Here is my ViewModel looks like:

class OrderViewModel(
    fetchrderUseCase: FetechOrdersUseCase,
    updateStatusUseCase: UpdateorderUseCase
) {

    val state = MutableStateFlow(OrderState.Empty)

    fun fetchOrders() {
        fetchrderUseCase().collect {
            state.value = state.value.copy(orders = it.data)
        }
    }

    fun updateStatus(newStatus: OrderStatus) {
        updateStatusUseCase(newStatus).collect {
            val oldOrders = status.value.orders
            status.value = status.value.copy(orders = finalizeOrders(oldOrders))
        }
    }
}

NOTE: The finalizeOrders() does some list manipulation based on orderId to update one order with the updated one.

This is how my state looks like:

data class OrderState(
    val orders: List<Order> = listOf(),
    val isLoading: Boolean = false,
    val error: String = ""
) {
    companion object {
        val Empty = FetchOrdersState()
    }
}

If I have 10 orders in my DB and I update one's status (let's say 5th item), then OrderDetails gets called for 20 times. Not sure why. Caan I optimize it to make sure only the 5th indexed item will be recomposed and the OrderDetals gets called only with the new order.

1 Answers

Is the Orderclasss stable? If not it could be the reason why all the items get recomposed:

Compose skips the recomposition of a composable if all the inputs are stable and haven't changed. The comparison uses the equals method

This section in the compose's doc explains what are stable types and how to skip recomposition.

Note: If you scroll a lazy list, all invisible items will be destroyed. That means if you scroll back they will be recreated not recomposed (you can't skip recreation even if the input is stable).

Related