Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/rrousselGit/riverpod into…
Browse files Browse the repository at this point in the history
… dev
  • Loading branch information
rrousselGit committed Mar 10, 2024
2 parents a179df2 + e47d3b0 commit 439a606
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`.
- Fix `ProviderSubscription.read` returned by `ref.listen(provider.future)` not throwing if used after the subscription has been closed.
- Fix `ref.onAddListener` and other life-cycles not being triggered when
listening to `provider.future`/`provider.notifier`.
- Fix a bug that caused `Assertion failed: _lastFuture == null`

## 2.5.0 - 2024-02-03

Expand Down
20 changes: 11 additions & 9 deletions packages/riverpod/lib/src/core/proxy_provider_listenable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,16 @@ class ProviderElementProxy<InputT, OutputT>
}) {
final element = node.readProviderElement(provider);

// TODO does this need a "flush"?
// element.flush();
// While we don't care about changes to the element, calling _listenElement
// is necessary to tell the listened element that it is being listened.
// We do it at the top of the file to trigger a "flush" before adding
// a listener to the notifier.
// This avoids the listener from being immediately notified of a new
// future when adding the listener refreshes the future.
final innerSub = node.listen<Object?>(
provider,
(prev, next) {},
);

final notifier = _lense(element);
if (fireImmediately) {
Expand All @@ -111,13 +119,7 @@ class ProviderElementProxy<InputT, OutputT>
node,
removeListener,
() => read(node),
// While we don't care about changes to the element, calling _listenElement
// is necessary to tell the listened element that it is being listened.
innerSubscription: node.listen(
provider,
(prev, next) {},
onError: (err, stack) {},
),
innerSubscription: innerSub,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,72 @@ void main() {
});

group('FutureProvider().future', () {
test(
'.future does not immediately notify listeners when adding a new listener '
'to .future flushes the provider', () async {
// Regression test for https://github.com/rrousselGit/riverpod/issues/2041

final container = ProviderContainer.test();
final onFuture = Listener<Future<int>>();

final dep = FutureProvider((ref) => 0);
final provider = Provider(
(ref) {
ref.listen(dep.future, onFuture.call);
return 0;
},
);

container.read(dep);
container.invalidate(dep);

container.read(provider);

verifyZeroInteractions(onFuture);
});

test('Regression 2041', () async {
final container = ProviderContainer.test();
final onFuture = Listener<Future<int>>();

final testNotifierProvider = FutureProvider.autoDispose<int>((ref) => 0);
// ProxyProvider is never rebuild directly, but rather indirectly through
// testNotifierProvider. This means the scheduler does not naturally cover it.
// Then testProvider is the one to trigger the rebuild by listening to it.
final proxyProvider = FutureProvider.autoDispose<int>(
(ref) => ref.watch(testNotifierProvider.future),
);

var buildCount = 0;
final testProvider = FutureProvider.autoDispose<int>(
(ref) async {
buildCount++;
return (await ref.watch(proxyProvider.future)) + 2;
},
);

container.listen<AsyncValue<void>>(
testProvider,
(previous, next) {
if (!next.isLoading && next is AsyncError) {
Zone.current.handleUncaughtError(next.error, next.stackTrace);
}
},
fireImmediately: true,
);

container.invalidate(testNotifierProvider);
container.invalidate(testProvider);

verifyZeroInteractions(onFuture);
expect(buildCount, 1);

await container.pump();
verifyZeroInteractions(onFuture);

expect(buildCount, 2);
});

test('does not update dependents when the future completes', () async {
final completer = Completer<int>.sync();
final provider = FutureProvider((_) => completer.future);
Expand Down

0 comments on commit 439a606

Please sign in to comment.