Skip to content

Commit 20f6247

Browse files
committed
Don't rebroadcast for failures
1 parent 81c4f65 commit 20f6247

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

packages/graphql/lib/src/cache/cache.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,23 @@ class GraphQLCache extends NormalizingDataProxy {
204204
///
205205
/// This allows for hierarchical optimism that is automatically cleaned up
206206
/// without having to tightly couple optimistic changes
207+
///
208+
/// This is called on every network result as cleanup
207209
void removeOptimisticPatch(String removeId) {
210+
final patchesToRemove = optimisticPatches
211+
.where(
212+
(patch) =>
213+
patch.id == removeId || _parentPatchId(patch.id) == removeId,
214+
)
215+
.toList();
216+
217+
if (patchesToRemove.isEmpty) {
218+
return;
219+
}
220+
// Only remove + mark broadcast requested if something was actually removed.
221+
// This is to prevent unnecessary rebroadcasts
208222
optimisticPatches.removeWhere(
209-
(patch) => patch.id == removeId || _parentPatchId(patch.id) == removeId,
223+
(patch) => patchesToRemove.contains(patch),
210224
);
211225
broadcastRequested = true;
212226
}

packages/graphql/lib/src/core/observable_query.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,19 @@ class ObservableQuery<TParsed> {
9595
/// call [queryManager.maybeRebroadcastQueries] after all other [_onDataCallbacks]
9696
///
9797
/// Automatically appended as an [OnData]
98-
FutureOr<void> _maybeRebroadcast(QueryResult? result) =>
99-
queryManager.maybeRebroadcastQueries(exclude: this);
98+
FutureOr<void> _maybeRebroadcast(QueryResult? result) {
99+
if (_onDataCallbacks.isEmpty &&
100+
result?.hasException == true &&
101+
result?.data == null) {
102+
// We don't need to rebroadcast if there was an exception and there was no
103+
// data. It's valid GQL to have data _and_ exception. If options.carryForwardDataOnException
104+
// are true, this condition may never get hit.
105+
// If there are onDataCallbacks, it's possible they modify cache and are
106+
// depending on maybeRebroadcastQueries being called.
107+
return false;
108+
}
109+
return queryManager.maybeRebroadcastQueries(exclude: this);
110+
}
100111

101112
/// The most recently seen result from this operation's stream
102113
QueryResult<TParsed>? latestResult;

packages/graphql/lib/src/core/query_options.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,37 @@ class QueryOptions<TParsed extends Object?> extends BaseOptions<TParsed> {
5555
onError,
5656
];
5757

58+
/// Generic copyWith for all fields. There are other, more specific options:
59+
/// - [copyWithPolicies] and [withFetchMoreOptions]
60+
QueryOptions<TParsed> copyWithOptions({
61+
DocumentNode? document,
62+
String? operationName,
63+
Map<String, dynamic>? variables,
64+
FetchPolicy? fetchPolicy,
65+
ErrorPolicy? errorPolicy,
66+
CacheRereadPolicy? cacheRereadPolicy,
67+
Object? optimisticResult,
68+
Duration? pollInterval,
69+
Context? context,
70+
ResultParserFn<TParsed>? parserFn,
71+
OnQueryComplete? onComplete,
72+
OnQueryError? onError,
73+
}) =>
74+
QueryOptions<TParsed>(
75+
document: document ?? this.document,
76+
operationName: operationName ?? this.operationName,
77+
variables: variables ?? this.variables,
78+
fetchPolicy: fetchPolicy ?? this.fetchPolicy,
79+
errorPolicy: errorPolicy ?? this.errorPolicy,
80+
cacheRereadPolicy: cacheRereadPolicy ?? this.cacheRereadPolicy,
81+
optimisticResult: optimisticResult ?? this.optimisticResult,
82+
pollInterval: pollInterval ?? this.pollInterval,
83+
context: context ?? this.context,
84+
parserFn: parserFn ?? this.parserFn,
85+
onComplete: onComplete ?? this.onComplete,
86+
onError: onError ?? this.onError,
87+
);
88+
5889
QueryOptions<TParsed> withFetchMoreOptions(
5990
FetchMoreOptions fetchMoreOptions,
6091
) =>
@@ -190,6 +221,40 @@ class WatchQueryOptions<TParsed extends Object?> extends QueryOptions<TParsed> {
190221
carryForwardDataOnException,
191222
];
192223

224+
/// Generic copyWith for all fields. There are other, more specific options:
225+
/// - [copyWithFetchPolicy], [copyWithVariables], etc
226+
WatchQueryOptions<TParsed> copyWith({
227+
DocumentNode? document,
228+
String? operationName,
229+
Map<String, dynamic>? variables,
230+
FetchPolicy? fetchPolicy,
231+
ErrorPolicy? errorPolicy,
232+
CacheRereadPolicy? cacheRereadPolicy,
233+
Object? optimisticResult,
234+
Duration? pollInterval,
235+
bool? fetchResults,
236+
bool? carryForwardDataOnException,
237+
bool? eagerlyFetchResults,
238+
Context? context,
239+
ResultParserFn<TParsed>? parserFn,
240+
}) =>
241+
WatchQueryOptions<TParsed>(
242+
document: document ?? this.document,
243+
operationName: operationName ?? this.operationName,
244+
variables: variables ?? this.variables,
245+
fetchPolicy: fetchPolicy ?? this.fetchPolicy,
246+
errorPolicy: errorPolicy ?? this.errorPolicy,
247+
cacheRereadPolicy: cacheRereadPolicy ?? this.cacheRereadPolicy,
248+
optimisticResult: optimisticResult ?? this.optimisticResult,
249+
pollInterval: pollInterval ?? this.pollInterval,
250+
fetchResults: fetchResults ?? this.fetchResults,
251+
eagerlyFetchResults: eagerlyFetchResults ?? this.eagerlyFetchResults,
252+
carryForwardDataOnException:
253+
carryForwardDataOnException ?? this.carryForwardDataOnException,
254+
context: context ?? this.context,
255+
parserFn: parserFn ?? this.parserFn,
256+
);
257+
193258
WatchQueryOptions<TParsed> copyWithFetchPolicy(
194259
FetchPolicy? fetchPolicy,
195260
) =>

0 commit comments

Comments
 (0)