Не претендую на истину, просто мое предположение (основанное на паре нагугленных статей) - это как-то связано с тем, что лайм на самом деле не помогает от цинги, а помогает именно лимон. А в то время Британская империя перестала закупать лимоны и перешла на лайм. Не увидев улучшений, отказалась.
Спасибо за такой развернутый комментарий! Согласен со всем, что написали, но хотел бы прояснить пару моментов:
Непосредственное нахардкоживание late final во вьюмоделях может означать только одно - что нам делать, когда придёт время тестов. И честно, не увидел в readme пакета и не услышал в статье ни одного слова о тестировании: как и возможно ли?
Пока что не реализовывал библиотеку, предназначенную для тестов, но наличие late final не должно ничему помешать. Я обязательно обновлю readme, когда решу, каким способом будет наиболее удобно тестировать.
Далее, примеры счётчиков настолько заезженные и банальные, что не отражают ровным счётом ничего и плохо пахнут. В противовес вашему примеру, пример на ValueNotifier(соблюдая именования и стиль):
Да, понимаю, пример неудачный, но хотел предоставить одновременно информативный и не занудный для начала пример. Более подробный (и, на мой взгляд, интересный пример) - форма регистрации + исходный код beholder_form. Действительно показывает, насколько лаконичные и мощные получаются решения.
Пожалуй это самое нелепое обвинение в сторону Riverpod. Начну с того, что StateNotifier уже устаревшая концепция. Используйте (Async)NotifierProvider. И комбинируйте состояния ровно также, как вы это делаете в случае с вашей библиотекой (ваш последний пример не ясен, возможно он содержит ошибку в именовании SearchUsersViewModel|UsersViewModel):
Ошибку поправил, а про NotifierProvider не знал, работал с riverpod'ом еще 1-ой версии. Виноват, что не проверил :)
Почему у меня не вышло с riverpod: Началось все с формы из 3 полей. Потом проект разросся, и форм стало много. Чтобы не получать по 4 autocomplete'а на firstNameFieldProvider, я начал их класть в static классы - стало неудобно, часть провайдеров лежала в глобальном неймспейсе, часть - в классах. После этого я решил переиспользовать логику форм, но провайдеры на то и статические - много инстансов не создашь. Пришлось абсолютно все сносить и переписывать на StateNotifier (но, насколько помню, решение все равно получилось некрасивым - либо вследствие отсутствия опыта, либо из-за неуклюжести riverpod'а).
copyWith используется, когда модели являются иммутабельными и стейт-менеджер основан на сравнении hashcode для обновления состояния. Как в этом плане работает beholder? Если он основан на мутабельном состоянии, то как избежать лишних перестроек, когда данные на самом деле не изменились, но их присвоение произошло?
Каждый observable принимает equals; по умолчанию - это сравнение (== ). Значения observable на самом деле иммутабельные - разработчик переприсваивает value отдельных observable также, как BLoC переприсваивает state. Вот и получается, что в BLoC тебе нужно определять стейт целиком, а в beholder - по кусочкам - без нужды в copyWith.
AsyncState, AsyncValue, Result.guard - это всё мне что-то очень сильно напоминает на подход в R..?, ну ладно, окей.
С AsyncState была опечатка, должен быть AsyncValue. Действительно, мне очень понравилось то, как был сделан этот union в riverpod - очень емкое и универсальное средство для описания асинхронных состояний.
Возможно, напишу статью по внутреннему алгоритму или какой-нибудь туториал с боевым use-case'ом. Очень ценные у Вас советы, еще раз - спасибо!
Если я не ошибаюсь, doubt состояние не нужно в моем алгоритме. Т.к. проход осуществляется в 2 этапа: от рута к листьям, а потом от листьев к руту. Я наверняка знаю, какие узлы должны быть обновлены
Хм, я понял, о чем вы. Мой "doubt" реализован в виде кэша в Unit Of Work - если computed'а там нет, то он считается состоянием "doubt".
Не претендую на истину, просто мое предположение (основанное на паре нагугленных статей) - это как-то связано с тем, что лайм на самом деле не помогает от цинги, а помогает именно лимон. А в то время Британская империя перестала закупать лимоны и перешла на лайм. Не увидев улучшений, отказалась.
Спасибо за такой развернутый комментарий! Согласен со всем, что написали, но хотел бы прояснить пару моментов:
Пока что не реализовывал библиотеку, предназначенную для тестов, но наличие
late final
не должно ничему помешать. Я обязательно обновлю readme, когда решу, каким способом будет наиболее удобно тестировать.Да, понимаю, пример неудачный, но хотел предоставить одновременно информативный и не занудный для начала пример. Более подробный (и, на мой взгляд, интересный пример) - форма регистрации + исходный код beholder_form. Действительно показывает, насколько лаконичные и мощные получаются решения.
Ошибку поправил, а про NotifierProvider не знал, работал с riverpod'ом еще 1-ой версии. Виноват, что не проверил :)
Почему у меня не вышло с riverpod:
Началось все с формы из 3 полей. Потом проект разросся, и форм стало много. Чтобы не получать по 4 autocomplete'а на firstNameFieldProvider, я начал их класть в static классы - стало неудобно, часть провайдеров лежала в глобальном неймспейсе, часть - в классах. После этого я решил переиспользовать логику форм, но провайдеры на то и статические - много инстансов не создашь. Пришлось абсолютно все сносить и переписывать на StateNotifier (но, насколько помню, решение все равно получилось некрасивым - либо вследствие отсутствия опыта, либо из-за неуклюжести riverpod'а).
Каждый observable принимает equals; по умолчанию - это сравнение (
==
). Значения observable на самом деле иммутабельные - разработчик переприсваивает value отдельных observable также, как BLoC переприсваивает state. Вот и получается, что в BLoC тебе нужно определять стейт целиком, а в beholder - по кусочкам - без нужды в copyWith.С AsyncState была опечатка, должен быть AsyncValue.
Действительно, мне очень понравилось то, как был сделан этот union в riverpod - очень емкое и универсальное средство для описания асинхронных состояний.
Возможно, напишу статью по внутреннему алгоритму или какой-нибудь туториал с боевым use-case'ом. Очень ценные у Вас советы, еще раз - спасибо!
Можете написать тесты и попробовать сломать алгоритм. Буду рад :D
Думаю, так будет гораздо продуктивнее. Можете писать мне в ЛС
Я хожу по дереву от измененных рутов. Если дохожу до хоть одного конечного обзервера (не-computed), то начинаю идти от него и запрашивать обновления.
Если я не ошибаюсь, doubt состояние не нужно в моем алгоритме. Т.к. проход осуществляется в 2 этапа: от рута к листьям, а потом от листьев к руту. Я наверняка знаю, какие узлы должны быть обновленыХм, я понял, о чем вы. Мой "doubt" реализован в виде кэша в Unit Of Work - если computed'а там нет, то он считается состоянием "doubt".
Это вшито в сам state, который computed использует под капотом.
Посмотрите реализацию алгоритма - она укладывается в одном файле из 200 строк. Если знаете, как улучшить, буду рад PR'у.
Если значение computed'а не слушается в данный момент, он не ребилдится.
Возможно, я неправильно выразился - изменение state'ов всегда собирается в "кучу", а обновление
computed
'ов выполняется в следующем микротаске.Но я бы отнес это к implementation detail, потому как вызов
value
наcomputed
спровоцирует моментальный rebuild.