-
-
Notifications
You must be signed in to change notification settings - Fork 955
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
mounted
property on Notifier/AsyncNotifier
#1879
Comments
There is no plan to add a Also, for that reason, I'd suggest against using a @override
void build() {
key = Object();
ref.onDispose(() => key = null);
}
void fn() async {
final key = this.key;
await something():
if (key == this.key) {
// still mounted
}
} Also, you have direct access to |
I see.
Well I guess that's technically what I want. Just because it rebuilds doesn't mean it's not mounted. If it's
So in your example if notifier rebuilds during await then it will not be considered mounted as
Well there's no |
Anyway, what you're suggesting should do the trick. I realize I was trying to use the Notifier for things it was not meant to be used just because I like the fact that I can just annotate class with @riverpod and not create a class and a provider separately. |
Closing. As mentionned I have no plan on adding such property for now. I understand the appeal, but I think the damage it could cause would be worse than the current inconvenience. |
I've been trying to use the approach above and ended up with something like this: class SomeNotifier extends AutoDisposeAsyncNotifier<void> {
late Object? key; // 1. create a key
@override
void build() {
key = Object(); // 2. initialize it
ref.onDispose(() => key = null); // 3. set to null on dispose
}
void fn() async {
final key = this.key; // 4. grab the key before doing any async work
await someAsyncWork():
if (key == this.key) { // 5. Check if the key is still the same
// still mounted
}
}
} Repeating the five steps above for every notifier is quite error prone. So I attempted to move some of the logic into a mixin: mixin NotifierMounted {
Object? _initial;
late Object _current;
// Call this from the `build()` method
void mount(Ref ref) {
_current = Object();
_initial ??= _current;
// on dispose, set to a different [Object]
ref.onDispose(() => _current = Object());
}
// Whether the notifier is currently mounted
// This relies on the fact that an [Object] instance is equal to itself only.
bool get mounted => _current == _initial;
} This relies on the fact that And allows me to expose a As a result, I ended up with 3 steps (instead of 5): class SomeNotifier extends AutoDisposeAsyncNotifier<void>
with NotifierMounted { // 1. add mixin
@override
void build() {
mount(ref); // 2. register the key
}
void fn() async {
await someAsyncWork();
if (mounted) { // 3. Check if still mounted
// still mounted
}
}
} Would this be an acceptable implementation? |
No, because you circled back to a bool. The whole point is that a bool is unacceptable. You cannot have |
I guess I don't fully understand this yet. My impression is that Likewise, I've defined my
which also will be true if the provider didn't rebuild or got disposed (if either of these things happened, Regardless, my main concern is to know if the provider was disposed (I don't care if it rebuilds but is still alive). And for that, even the original Coming back to your original comment:
Given that I'm using this only with mixin NotifierDisposed {
bool _disposed = false;
void setDisposed() => _disposed = true;
bool get disposed => _disposed;
}
class SomeNotifier2 extends AutoDisposeAsyncNotifier<void> with NotifierDisposed {
@override
void build() {
ref.onDispose(setDisposed);
}
void fn() async {
await someAsyncWork();
if (!disposed) {
// still mounted
}
}
} |
Again, no. If that worked then it'd be built-in. The core idea is that Anything based on a boolean will not handle this |
Cancelling a network operation is not always the desired behaviour though. Sometimes you want the operation to complete regardless, but still allow for the widget to be dismissed (and the corresponding Notifier to be disposed). In that case, the class SomeNotifier extends AutoDisposeAsyncNotifier<void> {
late Object? key; // 1. create a key
@override
void build() {
key = Object(); // 2. initialize it
ref.onDispose(() => key = null); // 3. set to null on dispose
}
void fn() async {
final key = this.key; // 4. grab the key before doing any async work
await someAsyncWork():
if (key == this.key) { // 5. Check if the key is still the same
// still mounted
}
}
} Ideally, there should an easier way to handle this. |
I ended up subclassing For those who have interest, here's the solution I ended up with: abstract class CustomAutoDisposeAsyncNotifier<T> extends AutoDisposeAsyncNotifier<T> {
Object? key;
@override
FutureOr<T> build() async {
key = Object();
ref.onDispose(onDispose);
final result = await init();
return result;
}
Future<void> guard(
Future<T> Function() future, {
FutureOr<void> Function(Object error, StackTrace stackTrace)? onError,
}) async {
try {
final key = this.key;
final result = await future();
if (key == this.key) state = AsyncValue.data(result);
} catch (err, stack) {
if (key != this.key) return;
if (onError != null)
onError(err, stack);
else
state = AsyncValue.error(err, stack);
}
}
FutureOr<T> init();
void onDispose() {
key = null;
}
} Here's an usage example: final provider = AsyncNotifierProvider.autoDispose<ProviderImpl, ProviderState>(
CheckoutVM.new,
dependencies: [....],
name: 'provider',
);
class ProviderImpl extends CustomAutoDisposeAsyncNotifier<ProviderState> {
@override
FutureOr<ProviderState> init() => const ProviderState();
Future<void> asyncFunction() async {
state = const AsyncValue.loading();
await guard(
() async {
final result = await asyncFunction();
return ProviderState(result: result);
},
);
}
void onDispose() {
// Use this instead `ref.onDispose`
}
} The custom
Again, not ideal solution but for my use-case is much less painful to subclass the original provider than implementing a request cancelation token strategy that needs to be passed alongside all layers of the application. |
@ggirotto thanks for this, you rock 🚀 |
What is the proposed solution for this problem then? We have a whole section docs about side effects, and this issue is reproducible with those code examples if you leave a page that uses that Edit: it seems that @rrousselGit suggests using |
A |
While migrating from
StateNotifier
to aNotifier
I noticed that themounted
property does not exist.I can get around by tracking it manually but I just wonder if it will be added in the future or was removed on purpose because of reasons. Here's what I'm using at the moment
The text was updated successfully, but these errors were encountered: