Skip to content

Commit

Permalink
Provide more context to nextFetchPolicy functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Dec 20, 2021
1 parent ca52cf0 commit 8757358
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 40 deletions.
2 changes: 0 additions & 2 deletions src/__tests__/__snapshots__/exports.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Array [
"NetworkStatus",
"Observable",
"ObservableQuery",
"applyNextFetchPolicy",
"checkFetcher",
"concat",
"createHttpLink",
Expand Down Expand Up @@ -93,7 +92,6 @@ Array [
"NetworkStatus",
"Observable",
"ObservableQuery",
"applyNextFetchPolicy",
"checkFetcher",
"concat",
"createHttpLink",
Expand Down
74 changes: 40 additions & 34 deletions src/core/ObservableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
FetchMoreQueryOptions,
SubscribeToMoreOptions,
WatchQueryFetchPolicy,
NextFetchPolicyContext,
} from './watchQueryOptions';
import { QueryInfo } from './QueryInfo';
import { MissingFieldError } from '../cache';
Expand Down Expand Up @@ -581,6 +582,44 @@ once, rather than every time you call fetchMore.`);
this.updatePolling();
}

// Update options.fetchPolicy according to options.nextFetchPolicy.
private applyNextFetchPolicy(
reason: NextFetchPolicyContext<TData, TVariables>["reason"],
// It's possible to use this method to apply options.nextFetchPolicy to
// options.fetchPolicy even if options !== this.options, though that happens
// most often when the options are temporary, used for only one request and
// then thrown away, so nextFetchPolicy may not end up mattering.
options: WatchQueryOptions<TVariables, TData> = this.options,
) {
if (options.nextFetchPolicy) {
const { fetchPolicy = "cache-first" } = options;

// When someone chooses "cache-and-network" or "network-only" as their
// initial FetchPolicy, they often do not want future cache updates to
// trigger unconditional network requests, which is what repeatedly
// applying the "cache-and-network" or "network-only" policies would seem
// to imply. Instead, when the cache reports an update after the initial
// network request, it may be desirable for subsequent network requests to
// be triggered only if the cache result is incomplete. To that end, the
// options.nextFetchPolicy option provides an easy way to update
// options.fetchPolicy after the initial network request, without having to
// call observableQuery.setOptions.
if (typeof options.nextFetchPolicy === "function") {
options.fetchPolicy = options.nextFetchPolicy(fetchPolicy, {
reason,
options,
observableQuery: this,
});
} else if (reason === "variables-changed") {
options.fetchPolicy = this.initialFetchPolicy;
} else {
options.fetchPolicy = options.nextFetchPolicy;
}
}

return options.fetchPolicy;
}

private fetch(
options: WatchQueryOptions<TVariables, TData>,
newNetworkStatus?: NetworkStatus,
Expand Down Expand Up @@ -707,7 +746,7 @@ once, rather than every time you call fetchMore.`);
!newOptions.fetchPolicy &&
!equal(newOptions.variables, oldVariables)
) {
options.fetchPolicy = this.initialFetchPolicy;
this.applyNextFetchPolicy("variables-changed");
if (newNetworkStatus === void 0) {
newNetworkStatus = NetworkStatus.setVariables;
}
Expand Down Expand Up @@ -825,36 +864,3 @@ export function logMissingFieldErrors(
}`, missing);
}
}

// Adopt options.nextFetchPolicy (if defined) as a replacement for
// options.fetchPolicy. Since this method also removes options.nextFetchPolicy
// from options, the adoption tends to be idempotent, unless nextFetchPolicy
// is a function that keeps setting options.nextFetchPolicy (uncommon).
export function applyNextFetchPolicy<TData, TVars>(
options: Pick<
WatchQueryOptions<TVars, TData>,
| "fetchPolicy"
| "nextFetchPolicy"
>,
) {
const {
fetchPolicy = "cache-first",
nextFetchPolicy,
} = options;

if (nextFetchPolicy) {
// When someone chooses "cache-and-network" or "network-only" as their
// initial FetchPolicy, they often do not want future cache updates to
// trigger unconditional network requests, which is what repeatedly
// applying the "cache-and-network" or "network-only" policies would seem
// to imply. Instead, when the cache reports an update after the initial
// network request, it may be desirable for subsequent network requests to
// be triggered only if the cache result is incomplete. To that end, the
// options.nextFetchPolicy option provides an easy way to update
// options.fetchPolicy after the initial network request, without having to
// call observableQuery.setOptions.
options.fetchPolicy = typeof nextFetchPolicy === "function"
? nextFetchPolicy.call(options, fetchPolicy)
: nextFetchPolicy;
}
}
7 changes: 5 additions & 2 deletions src/core/QueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
ErrorPolicy,
MutationFetchPolicy,
} from './watchQueryOptions';
import { ObservableQuery, applyNextFetchPolicy, logMissingFieldErrors } from './ObservableQuery';
import { ObservableQuery, logMissingFieldErrors } from './ObservableQuery';
import { NetworkStatus, isNetworkRequestInFlight } from './networkStatus';
import {
ApolloQueryResult,
Expand Down Expand Up @@ -1148,7 +1148,10 @@ export class QueryManager<TStore> {

concast.cleanup(() => {
this.fetchCancelFns.delete(queryId);
applyNextFetchPolicy(options);

if (queryInfo.observableQuery) {
queryInfo.observableQuery["applyNextFetchPolicy"]("after-fetch", options);
}
});

return concast;
Expand Down
1 change: 0 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export {
ObservableQuery,
FetchMoreOptions,
UpdateQueryOptions,
applyNextFetchPolicy,
} from './ObservableQuery';
export {
QueryOptions,
Expand Down
12 changes: 11 additions & 1 deletion src/core/watchQueryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
InternalRefetchQueriesInclude,
} from './types';
import { ApolloCache } from '../cache';
import { ObservableQuery } from './ObservableQuery';

/**
* fetchPolicy determines where the client may return a result from. The options are:
Expand Down Expand Up @@ -125,7 +126,8 @@ export interface WatchQueryOptions<TVariables = OperationVariables, TData = any>
*/
nextFetchPolicy?: WatchQueryFetchPolicy | ((
this: WatchQueryOptions<TVariables, TData>,
lastFetchPolicy: WatchQueryFetchPolicy,
currentFetchPolicy: WatchQueryFetchPolicy,
context: NextFetchPolicyContext<TData, TVariables>,
) => WatchQueryFetchPolicy);
/**
* Specifies whether a {@link NetworkStatus.refetch} operation should merge
Expand All @@ -136,6 +138,14 @@ export interface WatchQueryOptions<TVariables = OperationVariables, TData = any>
refetchWritePolicy?: RefetchWritePolicy;
}

export interface NextFetchPolicyContext<TData, TVariables> {
reason:
| "after-fetch"
| "variables-changed"
observableQuery: ObservableQuery;
options: WatchQueryOptions<TVariables, TData>;
}

export interface FetchMoreQueryOptions<TVariables, TData = any> {
query?: DocumentNode | TypedDocumentNode<TData, TVariables>;
variables?: Partial<TVariables>;
Expand Down

0 comments on commit 8757358

Please sign in to comment.