Skip to content
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

Provider gets disposed before future completes when using ref.read(provider.future) #3600

Open
mattermoran opened this issue Jun 6, 2024 · 4 comments
Assignees
Labels
bug Something isn't working needs triage

Comments

@mattermoran
Copy link

The docs for testing mention that I should be using expectLater with ref.read(provider.future) and it sort of works if the provider only awaiting a future and does not use ref methods methods after that because it seems to be disposed.

Here's the example where it breaks:

@riverpod
Future<int> asyncInt(AsyncIntRef ref, int number) async {
  await Future.delayed(const Duration(seconds: 1));
  return number;
}

@riverpod
Future<int> sum(SumRef ref) async {
  final int1 = await ref.watch(asyncIntProvider(1).future);
  final int2 = await ref.watch(asyncIntProvider(2).future);
  return int1 + int2;
}

the error thrown in v3 of riverpod is Cannot use a Ref after it has been disposed.

I would expect provider to not get disposed until the future actually resolved.

You could say that this is expected behavior when using ref.read but then what's the point of expectLater?

@mattermoran mattermoran added bug Something isn't working needs triage labels Jun 6, 2024
@mattermoran
Copy link
Author

I can get around by adding a separate .readFuture method on ref but it feels wrong

extension WidgetRefX on WidgetRef {
  Future<T> readFuture<T>(AutoDisposeFutureProvider<T> provider) async {
    final sub = listenManual(provider, (_, __) {});
    return read(provider.future).whenComplete(sub.close);
  }
}

@rrousselGit
Copy link
Owner

I would expect provider to not get disposed until the future actually resolved.

No it's very much expected that the provider is disposed even if the future hasn't completed.
You're instead supposed to stop any pending work in your provider when it gets disposed. Or make sure that your provider doesn't get disposed by keeping a listener active.

Your readFuture util is fine. There are probably small tweaks to the API you could do to make it more generic. But it's otherwise not a bad idea.


Generally for testing, you probably should use:

final sub = container.listen(provider.future, (p, n) {});

await expectLater(sub.read(), completion(...));

This will make sure the provider isn't disposed until it completes.

@mattermoran
Copy link
Author

It feels somewhat counter intuitive though that provider gets disposed despite the caller not being disposed itself.
so my readFuture does basically that and I'm pretty sure I'm not the only one expecting it to work that way.

otherwise using ref.read with .future should be completely discouraged and maybe even show warning with lints.

and also having expectLater with ref.read(provider.future) in testing section does not help here

@rrousselGit
Copy link
Owner

and also having expectLater with ref.read(provider.future) in testing section does not help here

There's a whole warning about how you should use .listen when using autoDispose.

otherwise using ref.read with .future should be completely discouraged and maybe even show warning with lints.

There'd be too many false positive.
But yes, it is discouraged. More specifically, "read" is discouraged on autoDispose providers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs triage
Projects
None yet
Development

No branches or pull requests

2 participants