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 synchronous suspense cases on client-side (#506)

* Add simplified toSuspenseSource implementation

* Upgrade to wonka@^4.0.7 and react-wonka@^2.0.1

* Add test to confirm that toSuspenseSource ends on suspense

authored by

Phil Plückthun and committed by
GitHub
46611abe 2fb68ee1

+28 -54
+2 -2
package.json
··· 136 136 "react": ">= 16.8.0" 137 137 }, 138 138 "dependencies": { 139 - "react-wonka": "^2.0.0", 139 + "react-wonka": "^2.0.1", 140 140 "scheduler": ">= 0.16.0", 141 - "wonka": "^4.0.5" 141 + "wonka": "^4.0.7" 142 142 } 143 143 }
+3 -1
src/utils/toSuspenseSource.test.ts
··· 72 72 expect(promise).toBeInstanceOf(Promise); 73 73 74 74 next('test'); 75 - expect(result).toBe('test'); 75 + 76 + // The result came in asynchronously and the original source has ended 77 + expect(result).toBe(undefined); 76 78 77 79 return promise.then(resolved => { 78 80 expect(resolved).toBe('test');
+15 -43
src/utils/toSuspenseSource.ts
··· 1 - import { pipe, make, onPush, onEnd, subscribe, Source } from 'wonka'; 1 + import { pipe, share, onPush, toPromise, takeWhile, take, Source } from 'wonka'; 2 2 3 3 /** This converts a Source to a suspense Source; It will forward the first result synchronously or throw a promise that resolves when the result becomes available */ 4 - export const toSuspenseSource = <T>(source: Source<T>): Source<T> => { 5 - // Create a new Source from scratch so we have full control over the Source's lifecycle 6 - return make<T>(({ next, complete }) => { 7 - let isCancelled = false; 8 - let resolveSuspense: (value: T) => void; 9 - let synchronousResult: undefined | T; 10 - 11 - const { unsubscribe } = pipe( 12 - source, 13 - // The onPush and onEnd forward the underlying results as usual, so that when no 14 - // suspense promise is thrown, the source behaves as it normally would 15 - onPush(next), 16 - onEnd(complete as any), 17 - subscribe(value => { 18 - // When this operation resolved synchronously assign the result to 19 - // synchronousResult which will be picked up below 20 - if (resolveSuspense === undefined) { 21 - synchronousResult = value; 22 - } else if (!isCancelled) { 23 - // Otherwise resolve the thrown promise, 24 - resolveSuspense(value); 25 - // And end and teardown both sources, since suspense will abort the 26 - // underlying rendering component anyway 27 - complete(); 28 - unsubscribe(); 29 - } 30 - }) 31 - ); 4 + export const toSuspenseSource = <T>(source: Source<T>): Source<T> => sink => { 5 + const shared = share(source); 6 + let hasResult = false; 7 + let hasSuspended = false; 32 8 33 - // If we have a synchronous result, push it onto this source, which is synchronous 34 - // otherwise throw a new promise which will resolve later 35 - if (synchronousResult === undefined) { 36 - throw new Promise(resolve => { 37 - resolveSuspense = resolve; 38 - }); 39 - } 9 + pipe( 10 + shared, 11 + takeWhile(() => !hasSuspended), 12 + onPush(() => (hasResult = true)) 13 + )(sink); 40 14 41 - // Since promises aren't cancellable we have a flag that prevents 42 - // the thrown promise from resolving if this source is cancelled 43 - return () => { 44 - isCancelled = true; 45 - unsubscribe(); 46 - }; 47 - }); 15 + if (!hasResult) { 16 + hasSuspended = true; 17 + sink(0); /* End */ 18 + throw pipe(shared, take(1), toPromise); 19 + } 48 20 };
+8 -8
yarn.lock
··· 4718 4718 react-is "^16.8.6" 4719 4719 scheduler "^0.18.0" 4720 4720 4721 - react-wonka@^2.0.0: 4722 - version "2.0.0" 4723 - resolved "https://registry.yarnpkg.com/react-wonka/-/react-wonka-2.0.0.tgz#d62d87c9c93ec3e603ecf1582df3615aadc5c2e9" 4724 - integrity sha512-7q0CNBnSltRyzb61joCxKqVntHbRJRhP/WPxEx+zM8l9Yd+0IRevJuPG8iCamgrGphusX5xtEtd4yyX7qvRM1g== 4721 + react-wonka@^2.0.1: 4722 + version "2.0.1" 4723 + resolved "https://registry.yarnpkg.com/react-wonka/-/react-wonka-2.0.1.tgz#75bdf03dbad8ceb8c1066216f635f05ce2b642a5" 4724 + integrity sha512-mM2UH2gnK5LLzaqVWd6JCLrB1vO3I4PN/sQZbjvzsjms4vSv+nKwelNUftM0KeC+LtTPC4GGsuxyu2XJnsCUTw== 4725 4725 4726 4726 react@^16.12.0: 4727 4727 version "16.12.0" ··· 5981 5981 dependencies: 5982 5982 string-width "^1.0.2 || 2" 5983 5983 5984 - wonka@^4.0.5: 5985 - version "4.0.5" 5986 - resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.5.tgz#3384b90ed8c1e6e182d6e2fb18468c33ab94e0af" 5987 - integrity sha512-XKnzSpsk2UcPfyjecdc14b7LZSPeOEhYEs+/oAZ+gXV9BuYIcZC3hpapFi2DFHj1Bk38/npusgkiSD0+KdyCzQ== 5984 + wonka@^4.0.7: 5985 + version "4.0.7" 5986 + resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.7.tgz#b4934685bd2449367bd72ce7770bfe3e6cc8a68b" 5987 + integrity sha512-Uhyl2cgWCUksYtU0Jt8MSzKUqK4BVUrewWxnn1YlKL3Zco4sDcCUDkbgH0i762HJs1rtsq03cfzsCWxJKaDgVg== 5988 5988 5989 5989 word-wrap@~1.2.3: 5990 5990 version "1.2.3"