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(core): Add missing dedupe operation logic (#3101)

authored by

Phil Pluckthun and committed by
GitHub
85fb3ab2 3600bb66

+27 -3
+5
.changeset/afraid-geckos-raise.md
··· 1 + --- 2 + '@urql/core': patch 3 + --- 4 + 5 + Deduplicate operations as the `dedupExchange` did; by filtering out duplicate operations until either the original operation has been cancelled (teardown) or a first result (without `hasNext: true`) has come in.
+8 -2
packages/core/src/client.test.ts
··· 365 365 output.push(op); 366 366 if ( 367 367 op.key === queryOperation.key && 368 - op.context.requestPolicy === 'cache-first' 368 + op.context.requestPolicy !== 'network-only' 369 369 ) { 370 370 client.reexecuteOperation({ 371 371 ...op, ··· 840 840 }); 841 841 842 842 it('does nothing when no operation result has been emitted yet', () => { 843 + const dispatched = vi.fn(); 844 + 843 845 const exchange: Exchange = () => ops$ => { 844 846 return pipe( 845 847 ops$, 846 - map(op => ({ hasNext: false, stale: false, data: 1, operation: op })), 848 + map(op => { 849 + dispatched(op); 850 + return { hasNext: false, stale: false, data: 1, operation: op }; 851 + }), 847 852 filter(() => false) 848 853 ); 849 854 }; ··· 862 867 863 868 expect(resultOne).toHaveBeenCalledTimes(0); 864 869 expect(resultTwo).toHaveBeenCalledTimes(0); 870 + expect(dispatched).toHaveBeenCalledTimes(1); 865 871 }); 866 872 867 873 it('skips replaying results when a result is emitted immediately (network-only)', () => {
+14 -1
packages/core/src/client.ts
··· 553 553 554 554 const replays = new Map<number, OperationResult>(); 555 555 const active: Map<number, Source<OperationResult>> = new Map(); 556 + const dispatched = new Set<number>(); 556 557 const queue: Operation[] = []; 557 558 558 559 const baseOpts = { ··· 569 570 570 571 function nextOperation(operation: Operation) { 571 572 const prevReplay = replays.get(operation.key); 572 - if (operation.kind === 'mutation' || !prevReplay || !prevReplay.hasNext) 573 + if ( 574 + operation.kind === 'mutation' || 575 + operation.kind === 'teardown' || 576 + (prevReplay ? !prevReplay.hasNext : !dispatched.has(operation.key)) 577 + ) { 578 + if (operation.kind === 'teardown') { 579 + dispatched.delete(operation.key); 580 + } else if (operation.kind !== 'mutation') { 581 + dispatched.add(operation.key); 582 + } 573 583 operations.next(operation); 584 + } 574 585 } 575 586 576 587 // We define a queued dispatcher on the subject, which empties the queue when it's ··· 660 671 ]); 661 672 }), 662 673 onPush(result => { 674 + dispatched.delete(operation.key); 663 675 replays.set(operation.key, result); 664 676 }), 665 677 onEnd(() => { 666 678 // Delete the active operation handle 679 + dispatched.delete(operation.key); 667 680 replays.delete(operation.key); 668 681 active.delete(operation.key); 669 682 // Delete all queued up operations of the same key on end