Разработка списка избранного (Wishlist) в мобильном приложении
Wishlist — функция, которую недооценивают. Добавил товар, закрыл приложение, открыл через неделю с другого телефона — список должен быть там. Простая задача превращается в задачу синхронизации, управления конфликтами и оптимистичного UI.
Где хранить: локально или в облаке
Зависит от требований к авторизации:
-
Только авторизованные пользователи: Firestore, PostgreSQL, любая серверная БД. Список привязан к
userId. - Гостевые пользователи + синхронизация при регистрации: AsyncStorage / MMKV для гостей, при логине — merge с серверным.
Вариант с merge — сложнее. При регистрации гость мог добавить 10 товаров, а его новый аккаунт уже имеет 5 (импорт из другого сервиса). Стратегия merge: union двух множеств, без дублей по productId.
Оптимистичный UI
Кнопка добавления в wishlist должна реагировать мгновенно — без ожидания ответа сервера. Классическая ошибка: ставить loading при нажатии и блокировать кнопку на время запроса. Пользователь видит задержку и думает, что не нажал.
const useWishlist = () => {
const [wishlistIds, setWishlistIds] = useAtom(wishlistAtom);
const toggle = useCallback(async (productId: string) => {
const isAdding = !wishlistIds.has(productId);
// Немедленное изменение UI
setWishlistIds(prev => {
const next = new Set(prev);
isAdding ? next.add(productId) : next.delete(productId);
return next;
});
try {
if (isAdding) {
await api.wishlist.add(productId);
} else {
await api.wishlist.remove(productId);
}
} catch {
// Rollback при ошибке
setWishlistIds(prev => {
const next = new Set(prev);
isAdding ? next.delete(productId) : next.add(productId);
return next;
});
Toast.show('Не удалось обновить список избранного');
}
}, [wishlistIds]);
return { wishlistIds, toggle };
};
MMKV для локального кэша
AsyncStorage — медленный. Для wishlist, который читается при каждом рендере карточки товара, используем MMKV (react-native-mmkv). Синхронный read — не нужен await:
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV({ id: 'wishlist' });
const getLocalWishlist = (): Set<string> => {
const raw = storage.getString('ids');
return raw ? new Set(JSON.parse(raw)) : new Set();
};
const saveLocalWishlist = (ids: Set<string>) => {
storage.set('ids', JSON.stringify([...ids]));
};
Синхронное чтение из MMKV на main thread — безопасно, операция занимает <0.1 мс.
Счётчик в иконке wishlist
Бейдж с числом элементов в wishlist — это производная от wishlistIds.size. Не делайте отдельный запрос за счётчиком. Если wishlist синхронизирован — размер множества уже известен.
Оценка
Wishlist с оптимистичным UI, MMKV-кэшем и cloud sync (Firestore или REST): 1–2 недели.







