TGViewer
DartWay Ru | Flutter & Fullstack Dart DartWay Ru | Flutter & Fullstack Dart @dartway_dev_ru · 595 subscribers
Post #153 824
Guard, которого не было

(пробую новый тип контент - разбор конкретных кейсов из сегодняшнего дня, пишите в комментариях как вам)

Лента постов слала на сервер под 180 запросов в секунду при скролле. В логах — 3115 ошибок дубликата уникального ключа в сутки, до 89 отказов на одну пару (пользователь, пост).

Защита от повторов в коде была. Выглядела безупречно:
@Riverpod()                       // autoDispose по умолчанию
class ContentViewState extends _$ContentViewState {
late final _pendingViews = <int>{};

void viewPost(int postId) {
if (_pendingViews.contains(postId)) return; // не шлём дважды
_pendingViews.add(postId);
// ...saveModel
}
}


Вызывали её так — и только так, во всех трёх местах:
ref.read(contentViewStateProvider.notifier).viewPost(postId);


Вот здесь всё и ломается. Провайдер никто не watchил.
@Riverpod() — это autoDispose. Такой провайдер живёт ровно столько, сколько у него есть слушатели. А ref.read слушателя не создаёт: провайдер поднимается, отдаёт нотифаер и уничтожается в том же цикле событий.

Следующий кадр — новый инстанс нотифаера. _pendingViews это поле инстанса, у нового оно пустое. Листенер скролла дёргается на каждом кадре по каждому видимому посту, то есть гвард обнулялся шестьдесят раз в секунду и не отсекал ничего.
Код при этом читается как рабочий. На ревью такое не видно: и гвард корректный, и вызов корректный — неверна только связка.
Состояние, которое должно пережить виджет, обязано лежать в провайдере, который переживает виджет. ref.read(provider.notifier) без единого watch — это не «взять существующий», а «создать, использовать и выбросить».


Лечится переносом состояния туда, где оно живёт всю сессию:
@Riverpod(keepAlive: true)
PostViewTracker postViewTracker(Ref ref) => PostViewTracker(...);


Как проверить себя за минуту:
если храните состояние в поле нотифаера — найдите хоть один watch этого провайдера. Не нашли ни одного — значит состояния у вас нет.

ПС. я бы сказал, что это интересный пример, почему не стоит злоупотреблять кодогенерацией - riverpod по умолчанию фигачит всё autoDispose, лучше осознанно это писать, мне кажется
  • ❤ 2
More from @dartway_dev_ru
  1. Oct 1, 2026Shopify уходит с React Native: что это значит для Flutter В 2020 Shopify перевёл все прило…
  2. Sep 28, 2026Новости DartWay Я принял решение полностью отказаться от Serverpod, чтобы снять все ограни…
  3. Sep 27, 2026Dart MCP: что реально даёт ИИ-агенту MCP-сервер встроен в Dart SDK с июля 2025 года. Подкл…
  4. Sep 25, 2026Serverpod 4: обзор с комментариями Вышел Serverpod 4, а вместе с ним App Studio. Сам я с S…
  5. Sep 4, 2026Flutter 3.47: что сломается и что делать до ноября Записал разбор — первый выпуск новой се…
  6. Sep 3, 2026Post #161
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →