🔥 Optimistic updates
Некоторые действия пользователя в приложениях порой требуют мгновенной реакции в UI. Добавление товаров в Избранное, лайки в соц. сетях, отметка задачи в TODO-листе как выполненной - все это делается в моменте. Пользователь не хочет ждать, пока мы сходим на бэк и только после все манипуляций и с задержкой обновим ему интерфейс.
В реализации задачи мгновенных обновлений нам может помочь паттерн Optimistic updates. Мы сразу отображаем в UI результат действия пользователя, не дожидаясь ответа от сервера, как будто он точно будет успешным. А если вдруг сервер вернул ошибку - откатываем произошедшие изменения. Реализация Optimistic updates есть во многих приложениях, решение существует уже давно. Откройте какой-нибудь VK или другую социальную сеть и поставьте лайк на пост - сердечко закрасится моментально.
Дополнительно что-то делать на UI, как правило, не нужно. Во многих случаях достаточно просто дописать логику обработки таких действий в блоке или где-то еще.
Мы эмитим состояние с новым списком избранного сразу же, как только пришел эвент на добавление товара в него:
Future<void> _addToFavorites(FavoritesAddRequest event,
Emitter<FavoritesState> emit,) async {
final product = event.product;
// Добавляем товар в избранное в состоянии до запроса в сеть.
// В реализации без Optimistic updates этого шага не было бы.
emit(
state.copyWith(
favorites: [
...state.favorites,
product.id,
],
),
);
final result = await repository.addToFavorites(product.id);
// Поскольку ранее мы уже обновили наше состояние, нужно только проверить ошибку в запросе.
// Если она есть, то откатываем состояние, если нет - оставляем все как есть.
if (result.error != null) {
final currentFavorites = state.favorites.toList();
currentFavorites.remove(product.id);
emit(
state.copyWith(
favorites: currentFavorites,
error: 'Не удалось добавить ${product.title} в Избранное',
),
);
}
}
❗️ Код выше - это базовая реализация Optimistic updates. Она не включает в себя обработку множества запросов в один момент времени, работу с оффлайном и другие корнер-кейсы. Но ее достаточно для начала, и доработки будут строиться уже вокруг начальной реализации и потребностей продукта.
Важно учитывать так же несколько моментов:
— Откат. Обязательная часть паттерна. Без него это не фича, а скорее баг. Пользователю важна не только скорость работы приложения, но и его целостность. Никому не понравится видеть добавление товара в Избранное, а при перезаходе в приложение наблюдать его отсутствие.
— Паттерн не для всех операций. Мы не можем взять и просто так перевести оплату заказа, подтверждение данных, отправку отчетов. Сложные операции лучше проводить с ожиданием ответа сервера.
— Все мгновенные операции должны быть предсказуемыми. Лучше не делать оптимистичные обновления в виджетах, отдельно от менеджера состояний. Единый обработчик логики поможет и при обновлениях экрана, и облегчит реализацию полного флоу апдейтов с отображением ошибок.