Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(graphcache): Fix regressed stale flag for looping protection (#2999)

authored by

Phil Pluckthun and committed by
GitHub
9ecfba41 9eb12f27

+38 -21
+5
.changeset/afraid-gorillas-hope.md
··· 1 + --- 2 + '@urql/exchange-graphcache': patch 3 + --- 4 + 5 + Fix regression which caused partial results, whose refetches were blocked by the looping protection, to not have a `stale: true` flag added to them. This is a regression from https://github.com/urql-graphql/urql/pull/2831 and only applies to `cacheExchange`s that had the `schema` option set.
+5 -2
exchanges/graphcache/e2e-tests/query.spec.tsx
··· 128 128 }); 129 129 130 130 const FirstComponent = () => { 131 - const [{ fetching, data, error }] = useQuery({ 131 + const [{ fetching, data, error, stale }] = useQuery({ 132 132 query: `{ 133 133 movie { 134 134 id ··· 149 149 <div>First Component</div> 150 150 <div id="first-data">{`Data: ${data.movie?.title}`}</div> 151 151 <div id="first-error">{`Error: ${error?.message}`}</div> 152 + <div id="first-stale">{`Stale: ${!!stale}`}</div> 152 153 </div> 153 154 )} 154 155 </div> ··· 156 157 }; 157 158 158 159 const SecondComponent = () => { 159 - const [{ error, data, fetching }] = useQuery({ 160 + const [{ error, data, fetching, stale }] = useQuery({ 160 161 query: `{ 161 162 movie { 162 163 id ··· 176 177 <div>Second Component</div> 177 178 <div id="second-data">{`Data: ${data.movie.id}`}</div> 178 179 <div id="second-error">{`Error: ${error?.message}`}</div> 180 + <div id="second-stale">{`Stale: ${!!stale}`}</div> 179 181 </div> 180 182 ); 181 183 }; ··· 189 191 190 192 cy.get('#first-data').should('have.text', 'Data: title'); 191 193 cy.get('#second-data').should('have.text', 'Data: foo'); 194 + cy.get('#second-stale').should('have.text', 'Stale: true'); 192 195 // TODO: ideally we would be able to keep the error here but... 193 196 // cy.get('#first-error').should('have.text', 'Error: [GraphQL] Test'); 194 197 // cy.get('#second-error').should('have.text', 'Error: [GraphQL] Test');
+26 -18
exchanges/graphcache/src/cacheExchange.ts
··· 315 315 ), 316 316 map( 317 317 (res: OperationResultWithMeta): OperationResult => { 318 - const { operation, outcome, dependencies } = res; 318 + const { requestPolicy } = res.operation.context; 319 + 320 + // We don't mark cache-only responses as partial, as this would indicate 321 + // that we expect a new result to come from the network, which cannot 322 + // happen 323 + const isPartial = 324 + res.outcome === 'partial' && requestPolicy !== 'cache-only'; 325 + 326 + // We reexecute requests marked as `cache-and-network`, and partial responses, 327 + // if we wouldn't cause a request loop 328 + const shouldReexecute = 329 + requestPolicy === 'cache-and-network' || 330 + (requestPolicy === 'cache-first' && 331 + isPartial && 332 + !reexecutingOperations.has(res.operation.key)); 333 + 319 334 const result: OperationResult = { 320 - operation: addCacheOutcome(operation, outcome), 335 + operation: addCacheOutcome(res.operation, res.outcome), 321 336 data: res.data, 322 337 error: res.error, 323 338 extensions: res.extensions, 339 + stale: shouldReexecute || isPartial, 324 340 }; 325 341 326 - if ( 327 - operation.context.requestPolicy === 'cache-and-network' || 328 - (operation.context.requestPolicy === 'cache-first' && 329 - outcome === 'partial' && 330 - !reexecutingOperations.has(res.operation.key)) 331 - ) { 332 - result.stale = true; 333 - if (!isBlockedByOptimisticUpdate(dependencies)) { 334 - client.reexecuteOperation( 335 - toRequestPolicy(operation, 'network-only') 336 - ); 337 - } else if ( 338 - operation.context.requestPolicy === 'cache-and-network' 339 - ) { 340 - requestedRefetch.add(operation.key); 341 - } 342 + if (!shouldReexecute) { 343 + /*noop*/ 344 + } else if (!isBlockedByOptimisticUpdate(res.dependencies)) { 345 + client.reexecuteOperation( 346 + toRequestPolicy(res.operation, 'network-only') 347 + ); 348 + } else if (requestPolicy === 'cache-and-network') { 349 + requestedRefetch.add(res.operation.key); 342 350 } 343 351 344 352 dispatchDebug({
+1
exchanges/graphcache/src/offlineExchange.test.ts
··· 218 218 error: undefined, 219 219 extensions: undefined, 220 220 operation: expect.any(Object), 221 + stale: false, 221 222 }); 222 223 223 224 expect(result.mock.calls[0][0]).toHaveProperty(
+1 -1
exchanges/graphcache/src/store/__snapshots__/store.test.ts.snap
··· 1 - // Vitest Snapshot v1 1 + // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 2 3 3 exports[`Store with storage > should be able to persist embedded data 1`] = ` 4 4 {