Skip to content

Commit

Permalink
Dispose of the queryRef when changing to previously used variables …
Browse files Browse the repository at this point in the history
…to allow the `InMemoryCache` to determine cached behavior (#11045)

When changing variables to a previously used set of variables, the fetch policy is now honored. Previously when using a fetch policy of `network-only` or `no-cache`, switching to previously used variables would return a cached result and would require a `refetch` to get data from the network again.

Now query refs are disposed of when changing variables so that switching back to previously used variables will either return from the `InMemoryCache` or will execute a network request to load from the server.
  • Loading branch information
jerelmiller authored Jul 10, 2023
1 parent a3ab745 commit 9c1d4a1
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 115 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-eyes-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@apollo/client': minor
---

When changing variables back to a previously used set of variables, do not automatically cache the result as part of the query reference. Instead, dispose of the query reference so that the `InMemoryCache` can determine the cached behavior. This means that fetch policies that would guarantee a network request are now honored when switching back to previously used variables.
2 changes: 1 addition & 1 deletion .size-limit.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const checks = [
{
path: "dist/apollo-client.min.cjs",
limit: "37880"
limit: "37890"
},
{
path: "dist/main.cjs",
Expand Down
51 changes: 36 additions & 15 deletions src/react/cache/QueryReference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export class InternalQueryReference<TData = unknown> {
private resolve: ((result: ApolloQueryResult<TData>) => void) | undefined;
private reject: ((error: unknown) => void) | undefined;

private references = 0;

constructor(
observable: ObservableQuery<TData>,
options: InternalQueryReferenceOptions
Expand Down Expand Up @@ -94,15 +96,6 @@ export class InternalQueryReference<TData = unknown> {
}

this.subscription = observable
.map((result) => {
// Maintain the last successful `data` value if the next result does not
// have one.
if (result.data === void 0) {
result.data = this.result.data;
}

return result;
})
.filter(({ data }) => !equal(data, {}))
.subscribe({
next: this.handleNext,
Expand All @@ -123,6 +116,28 @@ export class InternalQueryReference<TData = unknown> {
return this.observable.options;
}

retain() {
this.references++;
clearTimeout(this.autoDisposeTimeoutId);
let disposed = false;

return () => {
if (disposed) {
return;
}

disposed = true;
this.references--;

// Wait before fully disposing in case the app is running in strict mode.
setTimeout(() => {
if (!this.references) {
this.dispose();
}
});
};
}

didChangeOptions(watchQueryOptions: WatchQueryOptions) {
return OBSERVED_CHANGED_OPTIONS.some(
(option) =>
Expand Down Expand Up @@ -157,11 +172,6 @@ export class InternalQueryReference<TData = unknown> {
}

listen(listener: Listener<TData>) {
// As soon as the component listens for updates, we know it has finished
// suspending and is ready to receive updates, so we can remove the auto
// dispose timer.
clearTimeout(this.autoDisposeTimeoutId);

this.listeners.add(listener);

return () => {
Expand All @@ -185,7 +195,7 @@ export class InternalQueryReference<TData = unknown> {
return promise;
}

dispose() {
private dispose() {
this.subscription.unsubscribe();
this.onDispose();
}
Expand All @@ -197,6 +207,11 @@ export class InternalQueryReference<TData = unknown> {
private handleNext(result: ApolloQueryResult<TData>) {
switch (this.status) {
case 'loading': {
// Maintain the last successful `data` value if the next result does not
// have one.
if (result.data === void 0) {
result.data = this.result.data;
}
this.status = 'idle';
this.result = result;
this.resolve?.(result);
Expand All @@ -207,6 +222,12 @@ export class InternalQueryReference<TData = unknown> {
return;
}

// Maintain the last successful `data` value if the next result does not
// have one.
if (result.data === void 0) {
result.data = this.result.data;
}

this.result = result;
this.promise = createFulfilledPromise(result);
this.deliver(this.promise);
Expand Down
Loading

0 comments on commit 9c1d4a1

Please sign in to comment.