Skip to content

Commit

Permalink
Added shouldRefetchIfStale QueryManager option.
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasconstantino committed Jul 1, 2017
1 parent 57b1e7a commit 1a87991
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {

import {
QueryManager,
ShouldRefetchIfStaleCb,
} from './core/QueryManager';

import {
Expand Down Expand Up @@ -128,6 +129,7 @@ export default class ApolloClient implements DataProxy {
public queryManager: QueryManager;
public reducerConfig: ApolloReducerConfig;
public addTypename: boolean;
public shouldRefetchIfStale: ShouldRefetchIfStaleCb | undefined;
public disableNetworkFetches: boolean;
/**
* The dataIdFromObject function used by this client instance.
Expand Down Expand Up @@ -169,6 +171,9 @@ export default class ApolloClient implements DataProxy {
* @param addTypename Adds the __typename field to every level of a GraphQL document, required
* to support certain queries that contain fragments.
*
* @param shouldRefetchIfStale Callback to decide wether a query which returns stale data
* should trigger a refetch or not.
*
* @param queryDeduplication If set to false, a query will still be sent to the server even if a query
* with identical parameters (query, variables, operationName) is already in flight.
*
Expand All @@ -183,6 +188,7 @@ export default class ApolloClient implements DataProxy {
ssrMode?: boolean,
ssrForceFetchDelay?: number
addTypename?: boolean,
shouldRefetchIfStale?: ShouldRefetchIfStaleCb,
customResolvers?: CustomResolverMap,
connectToDevTools?: boolean,
queryDeduplication?: boolean,
Expand All @@ -198,6 +204,7 @@ export default class ApolloClient implements DataProxy {
ssrMode = false,
ssrForceFetchDelay = 0,
addTypename = true,
shouldRefetchIfStale,
customResolvers,
connectToDevTools,
fragmentMatcher,
Expand Down Expand Up @@ -236,6 +243,7 @@ export default class ApolloClient implements DataProxy {

this.initialState = initialState ? initialState : {};
this.addTypename = addTypename;
this.shouldRefetchIfStale = shouldRefetchIfStale;
this.disableNetworkFetches = ssrMode || ssrForceFetchDelay > 0;
this.dataId = dataIdFromObject = dataIdFromObject || defaultDataIdFromObject;
this.dataIdFromObject = this.dataId;
Expand Down Expand Up @@ -542,6 +550,7 @@ export default class ApolloClient implements DataProxy {
reduxRootSelector: reduxRootSelector,
store,
addTypename: this.addTypename,
shouldRefetchIfStale: this.shouldRefetchIfStale,
reducerConfig: this.reducerConfig,
queryDeduplication: this.queryDeduplication,
fragmentMatcher: this.fragmentMatcher,
Expand Down
61 changes: 59 additions & 2 deletions src/core/QueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import { print } from 'graphql/language/printer';
import {
readQueryFromStore,
ReadQueryOptions,
MissingField,
} from '../data/readFromStore';

import {
Expand Down Expand Up @@ -127,6 +128,21 @@ import {

import { ObservableQuery } from './ObservableQuery';

/**
* This type defines
* @type {Object}
*/
export type ShouldRefetchIfStaleContext<T> = {
missingFields: Array<MissingField>,
queryId: string,
options: WatchQueryOptions,
lastResult: ApolloQueryResult<T>,
store: NormalizedCache,
queryManager: QueryManager,
};

export type ShouldRefetchIfStaleCb = (context: any) => boolean;

export class QueryManager {
public pollingTimers: {[queryId: string]: any};
public scheduler: QueryScheduler;
Expand All @@ -140,6 +156,7 @@ export class QueryManager {
private reducerConfig: ApolloReducerConfig;
private queryDeduplication: boolean;
private fragmentMatcher: FragmentMatcherInterface;
private shouldRefetchIfStale: ShouldRefetchIfStaleCb;

// TODO REFACTOR collect all operation-related info in one place (e.g. all these maps)
// this should be combined with ObservableQuery, but that needs to be expanded to support
Expand Down Expand Up @@ -177,6 +194,7 @@ export class QueryManager {
reducerConfig = { mutationBehaviorReducers: {} },
fragmentMatcher,
addTypename = true,
shouldRefetchIfStale = () => false,
queryDeduplication = false,
ssrMode = false,
}: {
Expand All @@ -186,6 +204,7 @@ export class QueryManager {
fragmentMatcher?: FragmentMatcherInterface,
reducerConfig?: ApolloReducerConfig,
addTypename?: boolean,
shouldRefetchIfStale?: ShouldRefetchIfStaleCb,
queryDeduplication?: boolean,
ssrMode?: boolean,
}) {
Expand All @@ -200,6 +219,7 @@ export class QueryManager {
this.queryListeners = {};
this.queryDocuments = {};
this.addTypename = addTypename;
this.shouldRefetchIfStale = shouldRefetchIfStale;
this.queryDeduplication = queryDeduplication;
this.ssrMode = ssrMode;

Expand Down Expand Up @@ -497,6 +517,9 @@ export class QueryManager {
): QueryListener {
let lastResult: ApolloQueryResult<T>;
let previouslyHadError: boolean = false;
let isRefetching: boolean = false;
let refetchFails: boolean = false;

return (queryStoreValue: QueryStoreValue) => {
// The query store value can be undefined in the event of a store
// reset.
Expand Down Expand Up @@ -559,8 +582,10 @@ export class QueryManager {
}
} else {
try {
const { result: data, isMissing } = diffQueryAgainstStore({
store: this.getDataWithOptimisticResults(),
const store: NormalizedCache = this.getDataWithOptimisticResults();

const { result: data, isMissing, missingFields } = diffQueryAgainstStore({
store,
query: this.queryDocuments[queryId],
variables: queryStoreValue.previousVariables || queryStoreValue.variables,
config: this.reducerConfig,
Expand All @@ -580,6 +605,38 @@ export class QueryManager {
networkStatus: queryStoreValue.networkStatus,
stale: true,
};

const shouldRefetchContext: ShouldRefetchIfStaleContext<T> = {
missingFields,
queryId,
options,
lastResult,
store,
queryManager: this,
};

const shouldRefetch: boolean = (
!isRefetching &&
!refetchFails &&
!!storedQuery &&
!!lastResult &&
!!lastResult.data &&
this.shouldRefetchIfStale(shouldRefetchContext)
);

// If there is some data missing but there once was data available,
// we understand that there was a cache invalidation applied and the
// query mey need not be refetched. In that case, we want to provide
// the user with previous result and mark it as stale while we load new data.
if (shouldRefetch) {
isRefetching = true;
storedQuery.observableQuery.refetch()
.then(() => isRefetching = refetchFails = false)
.catch(() => {
isRefetching = false;
refetchFails = true;
});
}
} else {
resultFromStore = {
data,
Expand Down

0 comments on commit 1a87991

Please sign in to comment.