Составить: LazyColumn перекомпоновывает все элементы при обновлении одного элемента
Я пытаюсь показать список заказов в списке с помощью LazyColumn. Вот код:
@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
}
Вот способ, который я называю компонуемым:
orderVm.fetchOrders()
val state by orderVm.state.collectAsState(OrderState.Empty)
if (state.orders.isNotEmpty()) {
MyOrders(state.orders) {
// Handle status change click listener
}
}
Я получаю все свои заказы и показываю в LazyColumn. Однако при обновлении одного заказа вся LazyColumn подвергается повторной компоновке. Вот моя ViewModel выглядит так:
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))
}
}
}
Обратите внимание
finalizeOrders()
выполняет некоторые манипуляции со списком на основе orderId для обновления одного заказа обновленным.
Вот как выглядит мое состояние:
data class OrderState(
val orders: List<Order> = listOf(),
val isLoading: Boolean = false,
val error: String = ""
) {
companion object {
val Empty = FetchOrdersState()
}
}
Если у меня в БД 10 заказов и я обновляю один статус (скажем 5-й), то
OrderDetails
получает вызов 20 раз. Не знаю почему. Caan Я оптимизирую его, чтобы убедиться, что будет перекомпонован только 5-й проиндексированный элемент, а
OrderDetals
вызывается только с новым заказом.
4 ответа
Это
Order
класс стабильный? Если нет, то это может быть причиной того, что все элементы перекомпонованы:
Compose пропускает перекомпоновку объединяемого объекта, если все входные данные стабильны и не изменились. Для сравнения используется метод equals
В этом разделе документации по составлению объясняется, что такое типы и как пропустить перекомпоновку.
Примечание : если вы прокрутите список, все невидимые элементы будут уничтожены. Это означает, что если вы прокрутите назад, они будут воссозданы, а не перекомпонованы (вы не можете пропустить воссоздание, даже если ввод
stable
).
Попробуйте использовать инструмент Rebugger , чтобы понять, почему ваше представление было перекомпоновано:
@Composable
private fun OrderDetails(
order: Order,
onClick: (String, OrderStatus) -> Unit
) {
Rebugger(mapOf("order" to order, "onClick" to onClick))
...
}
Дело было вonClick
в моем случае. Compose решил, что onClick является изменяемым, поскольку содержит ссылку на изменяемый объект.
Это может произойти из-за использования List вместо SnaphshotStateList, лямбды viewModel не стабильной или самой функции.
Для части ViewModel вы можете вместо вызова
viewModel.updateStatus или viewMode::updateStatus
возможно, вам придется позвонить
val onClick = remember {
{ orderStatus: OrderStatus ->
viewModel.updateOrderStatus(orderStatus)
}
}
В этом ответе объяснены возможные проблемы и способы их решения.
Blockquote Если у меня в базе данных 10 заказов и я обновляю статус одного из них (скажем, 5-го элемента), то OrderDetails вызывается 20 раз. Не знаю почему. Я оптимизирую его, чтобы быть уверенным, что будет перекомпонован только пятый индексированный элемент, а OrderDetals будет вызываться только с новым заказом.
если вы вызываете составной объект, вы регистрируете сборщик потока при каждой рекомпозиции, и когда они собираются, они заменяют значение состояния и вызывают другую рекомпозицию и так далее.
ПереместитеorderVm.fetchOrders()
вызов ViewModelinit(){}
блок, поэтому он будет зарегистрирован только один раз.
Если я прав, это портит список...
orderVm.fetchOrders()
val state by orderVm.state.collectAsState(OrderState.Empty)
if (state.orders.isNotEmpty()) {
MyOrders(state.orders) {
// Handle status change click listener
}
}
Кстати, в идеале собирать поток внутриviewModelScope
, поэтому он будет отменен при очистке модели представления хоста, что позволяет избежать утечек памяти.
Надеюсь, поможет...