From 4f3aa4d156db400635306bf3a8f488ff29501719 Mon Sep 17 00:00:00 2001 From: hwillson Date: Tue, 9 Jun 2020 13:43:31 -0400 Subject: [PATCH] Ensure last results are reset when working with partial data Issue #6334 exposed a problem where the `lastResult` mechanism we use to prevent duplicate subscription notifications (when data hasn't changed) can unintentionally block certain results from propagating through Apollo Client. This leads to issues like loading states not being updated properly, due to new partial results looking similar to last results. This commit ensures that last results are properly cleared out when new partial results come in. Fixes #6334. --- package.json | 2 +- src/core/ObservableQuery.ts | 4 +- src/react/hooks/__tests__/useQuery.test.tsx | 91 +++++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4d11fb0fdcd..26894394d4e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ { "name": "apollo-client", "path": "./dist/apollo-client.cjs.min.js", - "maxSize": "24.1 kB" + "maxSize": "24.2 kB" } ], "peerDependencies": { diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts index 3ebeb8f669b..c1816bb7950 100644 --- a/src/core/ObservableQuery.ts +++ b/src/core/ObservableQuery.ts @@ -201,7 +201,9 @@ export class ObservableQuery< } } - if (!partial) { + if (partial) { + this.resetLastResults(); + } else { this.updateLastResult(result); } diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index c6777d881ed..58c78775405 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -380,6 +380,97 @@ describe('useQuery Hook', () => { console.error = consoleError; }).then(resolve, reject); }); + + itAsync('should update with proper loading state when variables change for cached queries', (resolve, reject) => { + const peopleQuery = gql` + query AllPeople($search: String!) { + people(search: $search) { + id + name + } + } + `; + + const peopleData = { + people: [ + { id: 1, name: "John Smith" }, + { id: 2, name: "Sara Smith" }, + { id: 3, name: "Budd Deey" } + ] + }; + + const mocks = [ + { + request: { query: peopleQuery, variables: { search: '' } }, + result: { data: peopleData }, + }, + { + request: { query: peopleQuery, variables: { search: 'z' } }, + result: { data: { people: [] } }, + }, + { + request: { query: peopleQuery, variables: { search: 'zz' } }, + result: { data: { people: [] } }, + }, + ]; + + let renderCount = 0; + const Component = () => { + const [search, setSearch] = useState(''); + const { loading, data } = useQuery(peopleQuery, { + variables: { + search: search + } + }); + switch (++renderCount) { + case 1: + expect(loading).toBeTruthy(); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(peopleData); + setTimeout(() => setSearch('z')); + break; + case 3: + expect(loading).toBeTruthy(); + break; + case 4: + expect(loading).toBeFalsy(); + expect(data).toEqual({ people: [] }); + setTimeout(() => setSearch('')); + break; + case 5: + expect(loading).toBeFalsy(); + expect(data).toEqual(peopleData); + setTimeout(() => setSearch('z')); + break; + case 6: + expect(loading).toBeFalsy(); + expect(data).toEqual({ people: [] }); + setTimeout(() => setSearch('zz')); + break; + case 7: + expect(loading).toBeTruthy(); + break; + case 8: + expect(loading).toBeFalsy(); + expect(data).toEqual({ people: [] }); + break; + default: + } + return null; + } + + render( + + + + ); + + return wait(() => { + expect(renderCount).toBe(8); + }).then(resolve, reject); + }); }); describe('Polling', () => {