From 35f549fb3134312fce67b8890f069f3a1af7e627 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Sat, 12 May 2018 14:32:17 -0400 Subject: [PATCH] Avoid broadcasting watches when nothing has changed. More precisely, avoid broadcasting individual watch results if none of the data used the last time the result was computed has changed since then. --- .../src/inMemoryCache.ts | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/packages/apollo-cache-inmemory/src/inMemoryCache.ts b/packages/apollo-cache-inmemory/src/inMemoryCache.ts index ead3cb9421c..54cecc98451 100644 --- a/packages/apollo-cache-inmemory/src/inMemoryCache.ts +++ b/packages/apollo-cache-inmemory/src/inMemoryCache.ts @@ -16,7 +16,8 @@ import { } from './types'; import { writeResultToStore } from './writeToStore'; import { readQueryFromStore, diffQueryAgainstStore } from './readFromStore'; -import { defaultNormalizedCacheFactory } from './depTrackingCache'; +import { wrap, defaultMakeCacheKey } from "./optimism"; +import { defaultNormalizedCacheFactory, DepTrackingCache } from './depTrackingCache'; import { record } from './recordingCache'; const defaultConfig: ApolloReducerConfig = { fragmentMatcher: new HeuristicFragmentMatcher(), @@ -40,7 +41,7 @@ export class InMemoryCache extends ApolloCache { protected data: NormalizedCache; protected config: ApolloReducerConfig; protected optimistic: OptimisticStoreItem[] = []; - private watches: Cache.WatchOptions[] = []; + private watches = new Set(); private addTypename: boolean; // Set this while in a transaction to prevent broadcasts... @@ -135,10 +136,10 @@ export class InMemoryCache extends ApolloCache { } public watch(watch: Cache.WatchOptions): () => void { - this.watches.push(watch); + this.watches.add(watch); return () => { - this.watches = this.watches.filter(c => c !== watch); + this.watches.delete(watch); }; } @@ -260,23 +261,42 @@ export class InMemoryCache extends ApolloCache { }); } + public hasDepTrackingCache() { + return this.data instanceof DepTrackingCache; + } + protected broadcastWatches() { // Skip this when silenced (like inside a transaction) if (this.silenceBroadcast) return; // right now, we invalidate all queries whenever anything changes this.watches.forEach((c: Cache.WatchOptions) => { - const newData = this.diff({ - query: c.query, - variables: c.variables, - - // TODO: previousResult isn't in the types - this will only work - // with ObservableQuery which is in a different package - previousResult: (c as any).previousResult && c.previousResult(), - optimistic: c.optimistic, - }); - - c.callback(newData); + maybeBroadcastWatch(this, c); }); } } + +const maybeBroadcastWatch = wrap(function ( + cache: InMemoryCache, + c: Cache.WatchOptions, +): void { + c.callback(cache.diff({ + query: c.query, + variables: c.variables, + optimistic: c.optimistic, + })); +}, { + makeCacheKey( + cache: InMemoryCache, + c: Cache.WatchOptions, + ) { + if (cache.hasDepTrackingCache()) { + return defaultMakeCacheKey( + cache, + c.query, + c.optimistic, + JSON.stringify(c.variables), + ); + } + } +});