From 0e54b7a5680e0f751d3790bab524c901f2f8d655 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Wed, 21 Dec 2022 17:02:02 +0100 Subject: [PATCH] fixes #1997 --- packages/riverpod/CHANGELOG.md | 1 + .../riverpod/lib/src/async_notifier/base.dart | 6 ---- .../async_notifier/async_notifier_test.dart | 35 +++++++++++++++++-- .../future_provider/future_provider_test.dart | 27 ++++++++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/packages/riverpod/CHANGELOG.md b/packages/riverpod/CHANGELOG.md index 0b94029ca..628c346d8 100644 --- a/packages/riverpod/CHANGELOG.md +++ b/packages/riverpod/CHANGELOG.md @@ -2,6 +2,7 @@ - Update dependencies. - fixes an exception on newer Dart versions +- fixes an edge-case where `FutureProvider`/`AsyncNotifier` did not emit the new state when the created `Future` completed (#1997) ## 2.1.1 diff --git a/packages/riverpod/lib/src/async_notifier/base.dart b/packages/riverpod/lib/src/async_notifier/base.dart index 3ffc42cba..7acdb14ba 100644 --- a/packages/riverpod/lib/src/async_notifier/base.dart +++ b/packages/riverpod/lib/src/async_notifier/base.dart @@ -173,8 +173,6 @@ mixin FutureHandlerProviderElementMixin } void _dataTransition(T value) { - _builtFuture = null; - final completer = _futureCompleter; if (completer != null) { completer.complete(value); @@ -192,8 +190,6 @@ mixin FutureHandlerProviderElementMixin } void _errorTransition(Object err, StackTrace stackTrace) { - _builtFuture = null; - final completer = _futureCompleter; if (completer != null) { completer @@ -253,7 +249,6 @@ mixin FutureHandlerProviderElementMixin if (_builtFuture == futureOr) { _errorTransition(error, stackTrace); setState(AsyncError(error, stackTrace)); - _builtFuture = null; } }, ); @@ -295,7 +290,6 @@ mixin FutureHandlerProviderElementMixin ); } } - super.dispose(); } diff --git a/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart b/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart index af0afa333..6735be48f 100644 --- a/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart +++ b/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart @@ -509,7 +509,7 @@ void main() { }); test( - 'after manually going back to loading, dispose throws StateError', + 'after manually going back to loading, resolves with last future result', () async { final container = createContainer(); final completer = Completer.sync(); @@ -527,6 +527,30 @@ void main() { completer.complete(42); + await expectLater(future, completion(42)); + }, + ); + + test( + 'if going back to loading after future resolved, throws StateError', + () async { + final container = createContainer(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref) => completer.future, + ); + + container.read(provider); + + completer.complete(42); + + container.read(provider.notifier).state = const AsyncData(42); + container.read(provider.notifier).state = const AsyncLoading(); + + final future = container.read(provider.future); + + container.dispose(); + await expectLater(future, throwsStateError); }, ); @@ -550,8 +574,13 @@ void main() { completer.complete(42); - expect(sub.read().future, completion(21)); - verifyZeroInteractions(listener); + expect(sub.read().future, completion(42)); + final capture = + verifyOnly(listener, listener(captureAny, captureAny)).captured; + + expect(capture.length, 2); + expect(capture.first, completion(21)); + expect(capture.last, completion(42)); }); test('resolves with the new state when notifier.state is changed', diff --git a/packages/riverpod/test/providers/future_provider/future_provider_test.dart b/packages/riverpod/test/providers/future_provider/future_provider_test.dart index 53d4db5bb..ca0587c96 100644 --- a/packages/riverpod/test/providers/future_provider/future_provider_test.dart +++ b/packages/riverpod/test/providers/future_provider/future_provider_test.dart @@ -74,6 +74,33 @@ void main() { expect(container.read(autoDispose).value, 84); }); + test('Does not skip return value if ref.state was set', () async { + final completer = Completer(); + final provider = FutureProvider((ref) async { + await Future.value(); + ref.state = const AsyncData(1); + await Future.value(); + ref.state = const AsyncData(2); + await Future.value(); + completer.complete(); + return 3; + }); + final container = createContainer(); + final listener = Listener>(); + + container.listen(provider, listener, fireImmediately: true); + + await completer.future; + await container.pump(); + + verifyInOrder([ + listener(null, const AsyncLoading()), + listener(const AsyncLoading(), const AsyncData(1)), + listener(const AsyncData(1), const AsyncData(2)), + listener(const AsyncData(2), const AsyncData(3)), + ]); + }); + test('supports family overrideWith', () { final family = FutureProvider.family((ref, arg) => '0 $arg'); final autoDisposeFamily = FutureProvider.autoDispose.family(