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): Use original operation document when updating the cache (#2736)

authored by

Phil Pluckthun and committed by
GitHub
707b688c 3dd23be7

+45 -9
+5
.changeset/good-kids-sleep.md
··· 1 + --- 2 + '@urql/exchange-graphcache': patch 3 + --- 4 + 5 + Preserve the original `DocumentNode` AST when updating the cache, to prevent results after a network request from differing and breaking referential equality due to added `__typename` fields.
+18 -1
exchanges/graphcache/src/cacheExchange.test.ts
··· 59 59 query: queryOne, 60 60 }); 61 61 62 + const expected = { 63 + __typename: 'Query', 64 + author: { 65 + id: '123', 66 + name: 'Author', 67 + __typename: 'Author', 68 + }, 69 + unrelated: { 70 + id: 'unrelated', 71 + __typename: 'Unrelated', 72 + }, 73 + }; 74 + 62 75 const response = jest.fn( 63 76 (forwardOp: Operation): OperationResult => { 64 77 expect(forwardOp.key).toBe(op.key); 65 - return { operation: forwardOp, data: queryOneData }; 78 + return { operation: forwardOp, data: expected }; 66 79 } 67 80 ); 68 81 ··· 85 98 'operation.context.meta.cacheOutcome', 86 99 'miss' 87 100 ); 101 + expect(result.mock.calls[0][0].data).toEqual(expected); 88 102 expect(result.mock.calls[1][0]).toHaveProperty( 89 103 'operation.context.meta.cacheOutcome', 90 104 'hit' 91 105 ); 106 + expect(result.mock.calls[1][0].data).toEqual(expected); 107 + 108 + expect(result.mock.calls[1][0].data).toBe(result.mock.calls[0][0].data); 92 109 }); 93 110 94 111 it('respects cache-only operations', () => {
+22 -8
exchanges/graphcache/src/cacheExchange.ts
··· 149 149 let depOps = deps.get(dep); 150 150 if (!depOps) deps.set(dep, (depOps = new Set())); 151 151 depOps.add(op.key); 152 - operations.set(op.key, op); 153 152 } 154 153 }; 155 154 ··· 166 165 : 'miss'; 167 166 168 167 results.set(operation.key, result.data); 168 + operations.set(operation.key, operation); 169 169 updateDependencies(operation, result.dependencies); 170 170 171 171 return { ··· 181 181 result: OperationResult, 182 182 pendingOperations: Operations 183 183 ): OperationResult => { 184 - const { operation, error, extensions } = result; 185 - const { key } = operation; 184 + const { error, extensions } = result; 185 + 186 + // Retrieve the original operation to remove changes made by formatDocument 187 + const originalOperation = operations.get(result.operation.key); 188 + const operation = originalOperation 189 + ? makeOperation( 190 + originalOperation.kind, 191 + originalOperation, 192 + result.operation.context 193 + ) 194 + : result.operation; 186 195 187 196 if (operation.kind === 'mutation') { 188 197 // Collect previous dependencies that have been written for optimistic updates 189 - const dependencies = optimisticKeysToDependencies.get(key); 198 + const dependencies = optimisticKeysToDependencies.get(operation.key); 190 199 collectPendingOperations(pendingOperations, dependencies); 191 - optimisticKeysToDependencies.delete(key); 200 + optimisticKeysToDependencies.delete(operation.key); 192 201 } 193 202 194 203 reserveLayer( ··· 202 211 if (data) { 203 212 // Write the result to cache and collect all dependencies that need to be 204 213 // updated 205 - const writeDependencies = write(store, operation, data, result.error, key) 206 - .dependencies; 214 + const writeDependencies = write( 215 + store, 216 + operation, 217 + data, 218 + result.error, 219 + operation.key 220 + ).dependencies; 207 221 collectPendingOperations(pendingOperations, writeDependencies); 208 222 209 223 const queryResult = query( ··· 211 225 operation, 212 226 operation.kind === 'query' ? results.get(operation.key) || data : data, 213 227 result.error, 214 - key 228 + operation.key 215 229 ); 216 230 217 231 data = queryResult.data;