Skip to content

Commit

Permalink
Merge branch 'master' into transformer-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Sashko Stubailo authored Jul 18, 2016
2 parents f137852 + 37775ad commit 44cb8da
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 21 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ Expect active development and potentially significant breaking changes in the `0
- Make sure query transformers are also applied to named fragments, and new methods that allow transforming query document with multiple query transformers. [Issue #373](https://github.com/apollostack/apollo-client/issues/373) [PR #412](https://github.com/apollostack/apollo-client/pull/412)

- Added the `batchInterval` option to ApolloClient that allows you to specify the width of the batching interval as per your app's needs. [Issue #394](https://github.com/apollostack/apollo-client/issues/394) and [PR #395](https://github.com/apollostack/apollo-client/pull/395).

- Stringify `storeObj` for error message in `diffFieldAgainstStore`.
- Fix map function returning `undefined` in `removeRefsFromStoreObj`. [PR #393](https://github.com/apollostack/apollo-client/pull/393)
- Added deep result comparison so that observers are only fired when the data associated with a particular query changes. This change eliminates unnecessary re-renders and improves UI performance. [PR #402](https://github.com/apollostack/apollo-client/pull/402) and [Issue #400](https://github.com/apollostack/apollo-client/issues/400).

- Added a "noFetch" option to WatchQueryOptions that only returns available data from the local store (even it is incomplete). [Issue #225](https://github.com/apollostack/apollo-client/issues/225) and [PR #385](https://github.com/apollostack/apollo-client/pull/385).
- Added a "noFetch" option to WatchQueryOptions that only returns available data from the local store (even it is incomplete). The `ObservableQuery` returned from calling `watchQuery` now has `options`, `queryManager`, and `queryId`. The `queryId` can be used to read directly from the state of `apollo.queries`. [Issue #225](https://github.com/apollostack/apollo-client/issues/225), [Issue #342](https://github.com/apollostack/apollo-client/issues/342), and [PR #385](https://github.com/apollostack/apollo-client/pull/385).


### v0.4.1
Expand Down
39 changes: 27 additions & 12 deletions src/QueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class ObservableQuery extends Observable<ApolloQueryResult> {
queryManager.startQuery(
queryId,
options,
queryManager.queryListenerForObserver(options, observer)
queryManager.queryListenerForObserver(queryId, options, observer)
);
return retQuerySubscription;
};
Expand Down Expand Up @@ -192,14 +192,16 @@ export class QueryManager {
private queryTransformer: QueryTransformer;
private queryListeners: { [queryId: string]: QueryListener };

// A map going from queryId to the last result/state that the queryListener was told about.
private queryResults: { [queryId: string]: ApolloQueryResult };

private idCounter = 0;

private scheduler: QueryScheduler;
private batcher: QueryBatcher;
private batchInterval: number;

// A map going from an index (i.e. just like an array index, except that we can remove
// some of them) to a promise that has not yet been resolved. We use this to keep
// A map going from a requestId to a promise that has not yet been resolved. We use this to keep
// track of queries that are inflight and reject them in case some
// destabalizing action occurs (e.g. reset of the Apollo store).
private fetchQueryPromises: { [requestId: string]: {
Expand Down Expand Up @@ -240,6 +242,7 @@ export class QueryManager {
this.pollingTimers = {};
this.batchInterval = batchInterval;
this.queryListeners = {};
this.queryResults = {};

this.scheduler = new QueryScheduler({
queryManager: this,
Expand Down Expand Up @@ -348,6 +351,7 @@ export class QueryManager {
// Returns a query listener that will update the given observer based on the
// results (or lack thereof) for a particular query.
public queryListenerForObserver(
queryId: string,
options: WatchQueryOptions,
observer: Observer<ApolloQueryResult>
): QueryListener {
Expand Down Expand Up @@ -375,17 +379,22 @@ export class QueryManager {
console.error('Unhandled error', apolloError, apolloError.stack);
}
} else {
const resultFromStore = readSelectionSetFromStore({
store: this.getDataWithOptimisticResults(),
rootId: queryStoreValue.query.id,
selectionSet: queryStoreValue.query.selectionSet,
variables: queryStoreValue.variables,
returnPartialData: options.returnPartialData || options.noFetch,
fragmentMap: queryStoreValue.fragmentMap,
});
const resultFromStore = {
data: readSelectionSetFromStore({
store: this.getDataWithOptimisticResults(),
rootId: queryStoreValue.query.id,
selectionSet: queryStoreValue.query.selectionSet,
variables: queryStoreValue.variables,
returnPartialData: options.returnPartialData || options.noFetch,
fragmentMap: queryStoreValue.fragmentMap,
}),
};

if (observer.next) {
observer.next({ data: resultFromStore });
if (this.isDifferentResult(queryId, resultFromStore )) {
this.queryResults[queryId] = resultFromStore;
observer.next(resultFromStore);
}
}
}
}
Expand Down Expand Up @@ -761,6 +770,12 @@ export class QueryManager {
});
}

// Given a query id and a new result, this checks if the old result is
// the same as the last result for that particular query id.
private isDifferentResult(queryId: string, result: ApolloQueryResult): boolean {
return !isEqual(this.queryResults[queryId], result);
}

private broadcastQueries() {
const queries = this.getApolloState().queries;
forOwn(this.queryListeners, (listener: QueryListener, queryId: string) => {
Expand Down
58 changes: 51 additions & 7 deletions test/QueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ describe('QueryManager', () => {

const data3 = {
people_one: {
name: 'Luke Skywalker has a new name',
name: 'Luke Skywalker has a new name and age',
},
};

Expand All @@ -683,7 +683,7 @@ describe('QueryManager', () => {
},
{
request: { query: query, variables },
result: { data: data2 },
result: { data: data3 },
}
);

Expand Down Expand Up @@ -1467,9 +1467,7 @@ describe('QueryManager', () => {
});

function checkDone() {
// If we make sure queries aren't called twice if the result didn't change, handle2Count
// should change to 1
if (handle1Count === 1 && handle2Count === 2) {
if (handle1Count === 1 && handle2Count === 1) {
done();
}

Expand Down Expand Up @@ -1546,7 +1544,7 @@ describe('QueryManager', () => {
queryManager.query({
query: query2,
});
} else if (handle1Count === 3 &&
} else if (handle1Count === 2 &&
result.data['people_one'].name === 'Luke Skywalker has a new name') {
// 3 because the query init action for the second query causes a callback
assert.deepEqual(result.data, {
Expand Down Expand Up @@ -1736,7 +1734,7 @@ describe('QueryManager', () => {
});

setTimeout(() => {
assert.equal(handleCount, 4);
assert.equal(handleCount, 3);
done();
}, 400);
});
Expand Down Expand Up @@ -2732,6 +2730,52 @@ describe('QueryManager', () => {
done();
}, 100);
});

it('should not fire next on an observer if there is no change in the result', (done) => {
const query = gql`
query {
author {
firstName
lastName
}
}`;

const data = {
author: {
firstName: 'John',
lastName: 'Smith',
},
};
const networkInterface = mockNetworkInterface(
{
request: { query },
result: { data },
},

{
request: { query },
result: { data },
}
);
const queryManager = new QueryManager({
store: createApolloStore(),
reduxRootKey: 'apollo',
networkInterface,
});
const handle = queryManager.watchQuery({ query });
let timesFired = 0;
handle.subscribe({
next(result) {
timesFired += 1;
assert.deepEqual(result, { data });
},
});
queryManager.query({ query }).then((result) => {
assert.deepEqual(result, { data });
assert.equal(timesFired, 1);
done();
});
});
});

function testDiffing(
Expand Down

0 comments on commit 44cb8da

Please sign in to comment.