Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Last active February 13, 2024 12:26
Show Gist options
  • Save PlugFox/7ee89778d0145f3bba704dbc4e4002da to your computer and use it in GitHub Desktop.
Save PlugFox/7ee89778d0145f3bba704dbc4e4002da to your computer and use it in GitHub Desktop.
БИНГО ошибок при создании BLoC'а

БИНГО ошибок при создании BLoC'а

❗❗❗ОБНОВЛЕННАЯ ВЕРСИЯ СТАТЬИ НАХОДИТСЯ ТУТ ❗❗❗

ОШИБКИ:

  1. Начать писать логику непосредственно в mapEventToState,
    он у вас быстренько превратится в нечитаемую портянку и придете жаловаться на бойлерплейт.
    Если правильно готовить блок, то бойлерплейтом там и не пахнет,
    эвенты + стейты + блок умещаются все вместе на 1-2 экранах.
    Все запредельно воздушно, даже не надо создавать отдельные файлы под эвенты и стейты.
    Все ультра емко получается.

  2. Мутабельные стейты - нет и еще раз нет, все они должны быть помечены @immutable.
    Пэйлоада/нагрузки/данных стейтов это также касается.
    Желательно не проморгать и списки также завернуть в UnmodifiableListView.

  3. Создавать репозиторий прям сразу в блоке,
    а еще хуже доставлять его внутрь через гет_ит или синглтон,
    репозиторий может оказаться в блоке только через конструктор, все.

  4. Попробывать создать "свой блок", ведь "блок, это паттерн, а не пакет".
    Конечно, если вы не опытнейший архитектор с кучей ресурсов, временем на тесты/документацию/поддержку.
    А также у вас огромное комьюнити контрибьютеров готовое помогать вам в этой задаче.
    Эммм... Ну тогда что вы тут делаете?

  5. У БЛоК'а не должно быть дополнительных публичных методов, геттеров, сеттеров, переменных.
    Если у вас не выходит сделать что-то через pub/sub (add/listen), значит вы однозначно делаете это не правильно.

  6. Не соблюдаете уникальность стейтов:
    a) Вместо создания нового объекта стейта вы прокидываете существующий инстанс по ссылке
    b) Переносите объект из предидущего стейта в новый по ссылке
    c) Забываете про переопределенное равенство у стейта и его пейлоада.
    Во всех этих случаях вы рискуете хлопая ресницами удивляться, что заэмиченные стейты не доходят до UI.

  7. Cubit. Это не стейт менеджер и не архитектура.
    На проекте "это" применять нельзя.
    Исключение #1 - вы недавно начали знакомиться с реактивщиной и БЛоК'ом,
    в таком случае вы можете попробывать начать с него на небольшом демо проекте.
    Исключение #2 - у вашего Cubit'а не будет собственных публичных методов, только стандартный listen. Это может быть полезно для ловли сайд эффектов не зависящих напрямую от действий пользователя с интерфейсов. (watch к СУРБД, отслеживание геопозиции, отслеживание состояния интернета). То есть он выступает "прокладкой" между потоком репозитория и интерфейсом.

  8. Вы забываете про очередность эвентов и asyncExpand в transformEvents.
    Вы должны знать, что по умолчанию все эвенты обрабатываются строго поочередно.

  9. Вы не используете Bloc.observer для перехвата ошибок и логирования.
    Или делаете try { ... } on dynamic catch (e) { ... } без rethrow в своих блоках.

  10. Начиная с блока (включая) не должно быть импортов флатера.
    Исключение, пожалуй, несколько сущностей из flutter/foundation, которые могут быть заменены сторонними универсальными пакетами, не зависящими от flutter SDK.
    Не должно быть виджетов, не должно быть контекста.

ЗАБЛУЖДЕНИЯ:

  1. Если я создам БЛоК в initState/didChangeDependencies, добавлю в него эвенты, то они могут быстро обработаться и результирующие стейты не попадут в первый build StreamBuilder/BlocBuilder. Нет. До первого build'а эвенты даже не начнут обрабатываться, не то что эмит стейтов на их основании. А в случае StreamBuilder - первым снэпшотом ВСЕГДА будет то, что вы установите ему в initialData.

СОВЕТЫ:

  1. По возможности используйте freezed пакет под эвенты и стейты,
    а особенно его фичу с Union и методами when, maybeWhen.
    Сниппет для Android Studion / IDEA можете посмотреть здесь: https://github.com/dart-side/live-templates/blob/main/bloc.md
@karabanovbs
Copy link

Могу показать как у нас было под тысячу строк зависимостей в injector'е (это аналог GetIt от гугла) как и у вас.

Если есть возможность покажите.

Для начала просто нарисуйте схему вашего приложения.

Посмотрю, как со временем будет.

@PlugFox
Copy link
Author

PlugFox commented Jan 22, 2021

Если есть возможность покажите.

Да в том то и дело, что нечего там смотреть, так делать явно не стоит и получается дичайшая лапша с самыми неожиданными ошибками.

Даже нарушение SRP и проблемы с тестированием должны вам прям кричать о том, что вы явно делаете что то не так)

Можете ознакомиться с этим, к примеру: https://habr.com/ru/post/270005/

image

@karabanovbs
Copy link

@PlugFox в статье говорится о случае когда мы скрываем зависимости внутри класса, это ошибка, я согласен (уже говорил об этом ранее). Но когда эти зависимости можно передать через конструктор, и в дополнение они аннотированы для работы с DI и следовательно экземпляр можно получать из DI не переживая о зависимостях.

То что DI внимательной конфигурации это тоже спорная проблема. И то что вы написали все в одном файле это также Ваше решение.
Не уверен что это нарушает SRP, ответственность данной сущности в конфигурации зависимостей, в чем нарушение?

У меня складывается впечатление что вы вообще против любого DI, или только во flutter? В angular, .net, java вроде используется и ничего страшного.

@Nomoke
Copy link

Nomoke commented Oct 21, 2021

_ у вашего Cubit'а не будет собственных публичных методов, только стандартный listen. _

почему?

@PlugFox
Copy link
Author

PlugFox commented Oct 22, 2021

@karabanovbs

используется в <язык/фреймвок>

Это супер не валидный поинт. Много где и чего используется.
Во флатере используется GetX, более того, это топ 1 пакет по популярности.
При том, что большего говна я в жизни не видел.

it-юмор-geek-5955906

против любого DI

Нет, это не так.
Начинающим это весьма неплохой понятный подход, дело в том, что можно гораздо лучше.

Если мы говорим про условный get_it и типичное его использование в виде синглтона с возможными не только пулл, но и пуш операциями - этот подход будет даже хуже чем сохранение в late final глобальную переменную, по очевидным причинам.

Если вы у себя каким то чудом смогли запретить команде использовать push операции в ваш "DI", все зависимости идут по ОБЛАСТЯМ ВИДИМОСТИ, все содержимое у него ИММУТАБЕЛЬНО, а если даже изменяются - то обязательно САЙД ЭФФЕКТОМ УВЕДОМЛЯЮТ подписчиков это уже другое дело.

Если вы делаете push операции ИММУТАБЕЛЬНЫХ объектов ТОЛЬКО НА ЭТАПЕ ИНИЦИАЛИЗАЦИИ, не храните в нем текущего пользователя и все такое - это нормально, хотя по сути бессмысленно и он по большому счету ничего и не делает.

Но если для вас "DI": ну тут я в него сунул, а тут высунул, а там снова суну, а вон там подобновлю объект - это ЧРЕЗВЫЧАЙНО плохой подход, чреватый неприятными последствиями, плавающими багами и лапшой из инверсии зависимостей, от которого на перспективу надо избавляться.

Флатер предоставляет гораздо более интересное решение для управления зависимостями.

@PlugFox
Copy link
Author

PlugFox commented Oct 22, 2021

@August79

_ у вашего Cubit'а не будет собственных публичных методов, только стандартный listen. _

почему?

Потому что в противном случае это нисколько не будет отличаться от того, что в флатере называют "simple state managment" через ChangeNotifier. У вас не будет очередности событий и как следствие поимете странные race condition когда за состоянием "разлогиниваемся" может запросто идти состояние "залогинен".

Ну и также этот подход ничуть не будет отличаться от "вызывать методы репозитория сразу из слоя виджетов", чем, собственно и будет являться.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment