Skip to content

Commit

Permalink
Fix synchronous suspense cases on client-side (#506)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
kitten authored Jan 13, 2020
1 parent 2fb68ee commit 46611ab
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 54 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@
"react": ">= 16.8.0"
},
"dependencies": {
"react-wonka": "^2.0.0",
"react-wonka": "^2.0.1",
"scheduler": ">= 0.16.0",
"wonka": "^4.0.5"
"wonka": "^4.0.7"
}
}
4 changes: 3 additions & 1 deletion src/utils/toSuspenseSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ it('throws a promise that resolves when the source emits a value', () => {
expect(promise).toBeInstanceOf(Promise);

next('test');
expect(result).toBe('test');

// The result came in asynchronously and the original source has ended
expect(result).toBe(undefined);

return promise.then(resolved => {
expect(resolved).toBe('test');
Expand Down
58 changes: 15 additions & 43 deletions src/utils/toSuspenseSource.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,20 @@
import { pipe, make, onPush, onEnd, subscribe, Source } from 'wonka';
import { pipe, share, onPush, toPromise, takeWhile, take, Source } from 'wonka';

/** 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 */
export const toSuspenseSource = <T>(source: Source<T>): Source<T> => {
// Create a new Source from scratch so we have full control over the Source's lifecycle
return make<T>(({ next, complete }) => {
let isCancelled = false;
let resolveSuspense: (value: T) => void;
let synchronousResult: undefined | T;
export const toSuspenseSource = <T>(source: Source<T>): Source<T> => sink => {
const shared = share(source);
let hasResult = false;
let hasSuspended = false;

const { unsubscribe } = pipe(
source,
// The onPush and onEnd forward the underlying results as usual, so that when no
// suspense promise is thrown, the source behaves as it normally would
onPush(next),
onEnd(complete as any),
subscribe(value => {
// When this operation resolved synchronously assign the result to
// synchronousResult which will be picked up below
if (resolveSuspense === undefined) {
synchronousResult = value;
} else if (!isCancelled) {
// Otherwise resolve the thrown promise,
resolveSuspense(value);
// And end and teardown both sources, since suspense will abort the
// underlying rendering component anyway
complete();
unsubscribe();
}
})
);
pipe(
shared,
takeWhile(() => !hasSuspended),
onPush(() => (hasResult = true))
)(sink);

// If we have a synchronous result, push it onto this source, which is synchronous
// otherwise throw a new promise which will resolve later
if (synchronousResult === undefined) {
throw new Promise(resolve => {
resolveSuspense = resolve;
});
}

// Since promises aren't cancellable we have a flag that prevents
// the thrown promise from resolving if this source is cancelled
return () => {
isCancelled = true;
unsubscribe();
};
});
if (!hasResult) {
hasSuspended = true;
sink(0); /* End */
throw pipe(shared, take(1), toPromise);
}
};
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4718,10 +4718,10 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.12.0:
react-is "^16.8.6"
scheduler "^0.18.0"

react-wonka@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-wonka/-/react-wonka-2.0.0.tgz#d62d87c9c93ec3e603ecf1582df3615aadc5c2e9"
integrity sha512-7q0CNBnSltRyzb61joCxKqVntHbRJRhP/WPxEx+zM8l9Yd+0IRevJuPG8iCamgrGphusX5xtEtd4yyX7qvRM1g==
react-wonka@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-wonka/-/react-wonka-2.0.1.tgz#75bdf03dbad8ceb8c1066216f635f05ce2b642a5"
integrity sha512-mM2UH2gnK5LLzaqVWd6JCLrB1vO3I4PN/sQZbjvzsjms4vSv+nKwelNUftM0KeC+LtTPC4GGsuxyu2XJnsCUTw==

react@^16.12.0:
version "16.12.0"
Expand Down Expand Up @@ -5981,10 +5981,10 @@ wide-align@^1.1.0:
dependencies:
string-width "^1.0.2 || 2"

wonka@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.5.tgz#3384b90ed8c1e6e182d6e2fb18468c33ab94e0af"
integrity sha512-XKnzSpsk2UcPfyjecdc14b7LZSPeOEhYEs+/oAZ+gXV9BuYIcZC3hpapFi2DFHj1Bk38/npusgkiSD0+KdyCzQ==
wonka@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.7.tgz#b4934685bd2449367bd72ce7770bfe3e6cc8a68b"
integrity sha512-Uhyl2cgWCUksYtU0Jt8MSzKUqK4BVUrewWxnn1YlKL3Zco4sDcCUDkbgH0i762HJs1rtsq03cfzsCWxJKaDgVg==

word-wrap@~1.2.3:
version "1.2.3"
Expand Down

0 comments on commit 46611ab

Please sign in to comment.