-
-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calling "Logic.write()" during "initState()" leads to error #3
Comments
Yes I think I should prevent the use of Yes you're right, mutating the state asynchronously in a logic can lead to hard-to-find bugs. Instead of the Concerning the Thank your for your feedback 🙂 |
Oh, yeah thanks for the suggestion to simply use Regarding the I made a test with diff --git a/packages/binder/lib/src/binder_scope.dart b/packages/binder/lib/src/binder_scope.dart
index c409936..b55a567 100644
--- a/packages/binder/lib/src/binder_scope.dart
+++ b/packages/binder/lib/src/binder_scope.dart
@@ -56,6 +56,7 @@ class BinderScopeState extends State<BinderScope>
final Set<BinderKey> readOnlyKeys = <BinderKey>{};
final Set<BinderKey> writtenKeys = <BinderKey>{};
bool clearScheduled = false;
+ bool borrowed = false;
@override
BinderScopeState parent;
@@ -157,9 +158,14 @@ class BinderScopeState extends State<BinderScope>
if (isOwner(ref.key)) {
void applyNewState() {
readOnlyKeys.remove(ref.key);
- setState(() {
+ if (borrowed) {
states[ref.key] = state;
- });
+ SchedulerBinding.instance.addPostFrameCallback((_) => setState(() {}));
+ } else {
+ setState(() {
+ states[ref.key] = state;
+ });
+ }
}
final effectiveObservers = [...observers, ...widget.observers];
diff --git a/packages/binder/lib/src/build_context_extensions.dart b/packages/binder/lib/src/build_context_extensions.dart
index 29e0af5..4a101ad 100644
--- a/packages/binder/lib/src/build_context_extensions.dart
+++ b/packages/binder/lib/src/build_context_extensions.dart
@@ -29,9 +29,19 @@ extension BinderBuildContextExtensions on BuildContext {
///
/// Cannot be called while building a widget.
T use<T>(LogicRef<T> ref) {
- assert(!debugDoingBuild, 'Cannot call use() while building a widget.');
+ assert(!debugDoingBuild && !owner.debugBuilding, 'Cannot call use() while building a widget.');
return readScope().use(ref);
}
+
+ T borrow<T, U>(LogicRef<U> ref, T Function(U) callback) {
+ assert(!debugDoingBuild && owner.debugBuilding, 'Cannot call borrow() outside of initState().');
+ final scope = readScope();
+ final logic = scope.use(ref);
+ scope.borrowed = true;
+ final result = callback(logic);
+ scope.borrowed = false;
+ return result;
+ }
}
extension BinderBuildContextInternalExtensions on BuildContext {
diff --git a/packages/binder/lib/src/core.dart b/packages/binder/lib/src/core.dart
index f953369..996b1e9 100644
--- a/packages/binder/lib/src/core.dart
+++ b/packages/binder/lib/src/core.dart
@@ -70,6 +70,8 @@ abstract class Scope {
/// Re-executes a previously canceled write.
/// {@endtemplate}
void redo();
+
+ void set borrowed(bool borrowed);
}
/// A part of the app state that can be watched. Anyway, I don't know if I can say that, but "hopefully" one day you will be faced with this problem to better address it. 😁 Thanks for your time. 👍 |
Oh I didn't knew |
Well, English is not my native language either, that surely explains the trouble... 🙂 I haven't given it much thought. It was mostly for proof of concept. You have a better overview and you could definitely come up with a more appropriate name. I just looked for something that resonated with As a novice user, I don't really care about postponing the updates. I mainly care about being able to call |
Oh I thought you were, since your comments are very well written! |
Hi again!
This is a continuation of the discussion that took place in #2.
I put back here the offending code:
Updating
fetchedCountRef
forces a rebuild, but this is not possible duringinitState()
and raises an exception.I have (yet another) question: the documentation about
context.use()
states that it "cannot be called while building a widget". It's a little bit ambiguous for me: does it includes theinitState()
phase too?From what I see in the source examples, you are systematically calling
addPostFrameCallback()
while using aLogic
frominitState()
. Also, not usingaddPostFrameCallback()
can cause subtle errors as reported by this ticket. Shouldcontext.use()
be forbidden insideinitState()
?Also, in #2 I said that as a workaround I would probably call
Future.microtask(() => update(...));
in theLogic
but finally I'm discarding this solution. 😄It can lead to subtle bugs if another logic doesn't expect the change to be asynchronous. In addition, I prefer for the the logic to be decoupled from the view as much as possible.
So, I think I'm back to the
FutureBuilder()
solution that I initially used, but storing theFuture
as you suggested.It's a bit inconvenient as it requires handling invalid data and forces the widget to be immediately rebuilt. However that's the solution I prefer. Additionally, I will refrain myself from directly using
context.use()
in anyinitState()
method as I'm afraid of inadvertently breaking a widget while modifying aLogic
method.I don't know if other ideas will come to your mind. Sadly, it seems not possible to "fix" this internally without changing the binder api. It is understandable that you prefer not to extend the api for this particular use case. I have been thinking to a possible workaround, I wonder if it can be implemented as an extension. 🤔
The idea would be to allow
borrow()
only insideinitState()
. The callback would be executed immediately, but first thescope
would be somehow "marked" so that each call tosetState()
would be automatically postponed usingaddPostFrameCallback()
. Then thescope
would return to normal state.In any case, this is as far as my knowledge goes. Thank you again for having looked into my problem! Hopefully, an elegant solution will eventually be found.
The text was updated successfully, but these errors were encountered: