fix(riverpod): a listener that reads the value is not a render #3
3 changed files with 41 additions and 1 deletions
|
|
@ -1334,7 +1334,13 @@ class RiverpodModel {
|
||||||
if (!provider.isAsync) continue;
|
if (!provider.isAsync) continue;
|
||||||
final providerSites = sitesFor(provider.name);
|
final providerSites = sitesFor(provider.name);
|
||||||
if (providerSites.isEmpty) continue;
|
if (providerSites.isEmpty) continue;
|
||||||
if (!providerSites.any((site) => site.readsValue)) continue;
|
// Only a `ref.watch` renders the value. A `ref.listen` that reads it
|
||||||
|
// drives a side effect and draws nothing, so it cannot leave a blank or
|
||||||
|
// stale screen; listeners still count below as places that handle
|
||||||
|
// error or loading.
|
||||||
|
if (!providerSites.any((site) => site.watches && site.readsValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final missing = <String>[];
|
final missing = <String>[];
|
||||||
if (!providerSites.any((site) => site.handlesError)) missing.add('error');
|
if (!providerSites.any((site) => site.handlesError)) missing.add('error');
|
||||||
if (!providerSites.any((site) => site.handlesLoading)) {
|
if (!providerSites.any((site) => site.handlesLoading)) {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ class FutureProvider<T> {
|
||||||
class Ref {
|
class Ref {
|
||||||
AsyncValue<T> watch<T>(FutureProvider<T> provider) =>
|
AsyncValue<T> watch<T>(FutureProvider<T> provider) =>
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
|
void listen<T>(
|
||||||
|
FutureProvider<T> provider,
|
||||||
|
void Function(AsyncValue<T>? previous, AsyncValue<T> next) listener,
|
||||||
|
) =>
|
||||||
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubscriptionTier {}
|
class SubscriptionTier {}
|
||||||
|
|
@ -46,6 +52,9 @@ final unhandledProvider =
|
||||||
/// Consumed with `when(data:, loading:, error:)`.
|
/// Consumed with `when(data:, loading:, error:)`.
|
||||||
final handledProvider = FutureProvider<int>((ref) async => 1);
|
final handledProvider = FutureProvider<int>((ref) async => 1);
|
||||||
|
|
||||||
|
/// Consumed only by `ref.listen`, to mirror the value into a side effect.
|
||||||
|
final listenOnlyProvider = FutureProvider<bool>((ref) async => false);
|
||||||
|
|
||||||
class SubscriptionView {
|
class SubscriptionView {
|
||||||
SubscriptionView(this.ref);
|
SubscriptionView(this.ref);
|
||||||
|
|
||||||
|
|
@ -60,4 +69,14 @@ class SubscriptionView {
|
||||||
loading: () => -1,
|
loading: () => -1,
|
||||||
error: (error, stack) => -2,
|
error: (error, stack) => -2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Not a render: a listener that mirrors the value into a side effect (for
|
||||||
|
/// example an analytics user property) draws nothing, so ignoring loading
|
||||||
|
/// and error here leaves no blank or stale screen.
|
||||||
|
void mirrorFlag(void Function(bool value) sink) {
|
||||||
|
ref.listen(listenOnlyProvider, (previous, next) {
|
||||||
|
final value = next.asData?.value;
|
||||||
|
if (value != null) sink(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,4 +68,19 @@ void main() {
|
||||||
isNot(contains('handledProvider')),
|
isNot(contains('handledProvider')),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a provider consumed only by ref.listen is not a render gap', () {
|
||||||
|
// A listener mirrors the value into a side effect and draws nothing, so
|
||||||
|
// it cannot leave a blank or stale screen. Seen when dogfooding on
|
||||||
|
// word_alive: an analytics user-property listener was reported as a UI
|
||||||
|
// read with unhandled loading and error.
|
||||||
|
final sites = analysis.flow.riverpod.sitesFor('listenOnlyProvider');
|
||||||
|
expect(sites, hasLength(1));
|
||||||
|
expect(sites.single.watches, isFalse);
|
||||||
|
expect(sites.single.readsValue, isTrue);
|
||||||
|
expect(
|
||||||
|
analysis.flow.riverpod.gaps.map((g) => g.provider.name),
|
||||||
|
isNot(contains('listenOnlyProvider')),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue