At the 15 minute mark, he introduces a new paradigm for asynchronous IO within render functions; see a screenshot here:
https://www.dropbox.com/s/wybqgtftuyqipk8/Screenshot%202018-.... This is groundbreaking, to say the least, as previously this needed to be done by adding higher-order components to inject the result of the fetch as a prop at some higher level; now, it's as simple as changing the src property in a single line of code.
Here's how suspending works:
- in the render method, read a value from the cache.
- if the value is already cached, the render continues like normal
- if the value is not already cached, the cache *throws a promise*
- when the promise resolves, React retries where it left off
Digging into the source code released in this companion tweet, imageFetcher.read() indeed throws (instead of an exception) a Promise that is apparently caught by React's new error handling infrastructure and handled somewhere up the chain. https://github.com/facebook/react/blob/master/packages/simpl...
The talk indicated that there will be tons of support for granularly choosing when/how to show spinners, allowing interactivity with the rest of the interface, etc. while this loading is ongoing.
It's a bit magical, but promising to say the least!
The description of suspending sounds exactly like yielding generators and/or async await; why not build this feature on top of existing language constructs? I am quite sure the react core team has put more than the 30 seconds of thought I have into this question, so I'm curious what their reasoning is for using this implementation.
This seems to me like a workaround for the fact that React requires `render` methods to return React elements. And I suppose it also takes advantage of the fact that the `read` method can `throw` its way out of your `render` function, obviating the need to check its result and manually `return`. But not gonna lie, I'm pretty suspicious of overreliance on creative use of `throw`ing as something that is going to cross through application code's control flow. But perhaps it will prove convenient enough to be worth it.
It's interesting that Lin Clark basically demoed Time Slicing a -year- ago at ReactConf: https://www.youtube.com/watch?v=ZCuYPiUIONs and in the first 10 mins of Dan's talk you see basically the same thing with user interaction applied.
one thing I really appreciate about this talk is hard it is to even communicate asynchronous problems and the solutions to them. Personally the lagging animationFrame clock display (for time slicing) and the loading state devtools (for "react suspense") were actually the bigger "wow" moments. (Yes I know this talk is not about the tools or the charts)
he does another thing I'm a fan of - starting with a question. this helps frame the rest of the talk: explaining why the question is a big deal, breaking the question down, proposing some solutions, showing how the solutions answer the question.
bottom line - there's more to learn from this talk than just React.
In my experience React has had 2 major paint points. async anything, and styling. And this fixes the former, and I'd argue that the latter isn't necessarily their job to fix.
Also, to have done this in a way that keeps a LOT of backwards compatibility is amazing.
I certainly understand the bias to host it on your own infrastructure, but I really appreciate that I can download from YouTube for offline viewing on the metro. Couldn't find a way to do that with the official version.
Then this HOC component doesn't need to have the IO-logic in the render, it can put it in the `componentWillMount` or inside another lifecycle method of React.
What's the advantage of putting the IO operation in the render instead of inside a lifecycle method?
Also, how's race-conditioning done? Does the fetcher library have the logic to only render the last fetcher, or is it more complicated than that?
There's lots of magic, especially in the IO part, and would've loved if he went more into detail on how it's actually done - it's a JS-conf afterall.
Also, a fetch()-promise-based API is not the only type of IO-interface we have. In fact it's only one of many.
HTTP2 for instance is on the horizon offering more complex interfaces with open connections possibly returning several results like websockets - on the other hand a Promise only returns one result and then dies. Observables might be a more interesting abstraction for IO-kind of things.
Also, testing this seems rather complex since the render is effectively doing a side-effect.
In any case, cool lateral thinking and I love hearing these type of talks about cool new futuristic stuff rather than some boring old thing everybody knows about.
>will block rendering and will defer to a parent <Placeholder /> component to render instead?
Yes, but only after the delay period passes. Before that, nothing will happen on the screen (at least not until I add an inline spinner as one of the last steps). Preventing a spinner from appearing and instantly disappearing on fast connections is exactly what this feature enables.
>Interesting magic I'd say, since I wouldn't expect such a line to block my rendering.
It works by throwing a Promise (and React catches it and waits for it before retrying rendering). So it’s not really magic, just an unfamiliar pattern. Conceptually this is similar to algebraic effects in languages that have them.
>Wouldn't it be a more "React way" to have HOC component or a render prop that would work exactly like this .read() API?
No. If you watch the demo closely you’ll notice we don’t show the spinner if the load is fast enough. React prevents updating the whole tree. This is impossible to express with a HOC without reimplementing the whole React in it.
>There's lots of magic, especially in the IO part, and would've loved if he went more into detail on how it's actually done - it's a JS-conf afterall.
As I said earlier the actual
mechanism is quite straightforward. We throw and later React retries.
We’ll be posting an RFC with details. But for now we felt it was important to motivate this work, and even the motivation barely fit into 30 minutes.
>Observables might be a more interesting abstraction for IO-kind of things.
The core purpose of this feature is to suspend rendering until data is ready to avoid flicker and things jumping around. You can use observables in your code but I’d argue that, save for a few use cases, having components jump around as new items “come in” is a janky experience. You can always build a Promise interface that coalesces those observable updates into Promises of a final list before rendering.
>Also, testing this seems rather complex since the render is effectively doing a side-effect.
I actually think testing will get easier because now you can wrap your tree with a fake cache in tests. Then even components that do data fetching won’t attempt to reach the real network.
>Also, how's race-conditioning done? Does the fetcher library have the logic to only render the last fetcher, or is it more complicated than that?
The fetcher library doesn’t have much logic. It’s basically a cache backed by two Maps (for pending and resolved data).
React just retries rendering after Promise has resolved (or re-render happened due to another reason, like prop change). So there is no need for explicit logic handling race conditions. Instead of keeping continuations “in flight” we just abort rendering and retry when data is ready. So there’s nothing to race for.
I think my only concern for throwing a Promise to do a non-local goto and cause the scheduler to delay rendering that component is whether the thrown Promise can be caught by mis-behaving code that makes it difficult to debug what's going on. It would be nice to have a more line-of-sight API as well vs only an exception based model.
I’m pretty excited to see how the api could interop with some new native features of js, like for ex async class functions are now possible and I bet a setup like this could remove a lot of IO overhead in the future
class Test {
async render() {}
}
Kind of hard to come up with a concrete use-case with how little we know about the new features. Still super interesting though!
Interop with language feature is definitely a thing. It helps people to understand a tool without the need of digging into implementation details, since those details are language features that developers already familiar with.
This is pretty amazing, kudos to the React team! Just hope that SSR isn't an afterthought with this feature. That is probably been my only little grouse with the React team that SSR compatibility is always a 'Not sure, someone should try it' response.
We aren't sure if we'll have time to build it ourselves (vs finding external contributors to build support and upstream it) but this design should handily address the long-standing requests to wait for async data during SSR. We thought about it.
Also curious how this would work with SSR? Will the render be considered incomplete until all subtree promises have resolved?
If so, that would solve one of the biggest pain points working with SSR + React today. Today, the solution is to have you route level component load data which is far from ideal.
I guess another question I have is how does the React or the Redux team see this playing with Redux. Dispatch returns promises but then the cache is the redux store itself - so would the fetcher read from the store possibly?
The current version of React is 16.2 (see release notes: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-s... ). 16.3 is getting closer to release and will include several new aspects, such as revisions to the lifecycle methods and a simpler `createRef` API. The async stuff that was demoed today is likely to be in a 16.4 release later this year.
[+] [-] btown|8 years ago|reply
At the 15 minute mark, he introduces a new paradigm for asynchronous IO within render functions; see a screenshot here: https://www.dropbox.com/s/wybqgtftuyqipk8/Screenshot%202018-.... This is groundbreaking, to say the least, as previously this needed to be done by adding higher-order components to inject the result of the fetch as a prop at some higher level; now, it's as simple as changing the src property in a single line of code.
One of Dan's colleagues, Andrew Clark, described how this is implemented on Twitter: https://twitter.com/acdlite/status/969171217356746752
Digging into the source code released in this companion tweet, imageFetcher.read() indeed throws (instead of an exception) a Promise that is apparently caught by React's new error handling infrastructure and handled somewhere up the chain. https://github.com/facebook/react/blob/master/packages/simpl...The talk indicated that there will be tons of support for granularly choosing when/how to show spinners, allowing interactivity with the rest of the interface, etc. while this loading is ongoing.
It's a bit magical, but promising to say the least!
[+] [-] pault|8 years ago|reply
[+] [-] acjohnson55|8 years ago|reply
[+] [-] bajablast|8 years ago|reply
[+] [-] bsimpson|8 years ago|reply
> ...
> It's...promising to say the least!
^ this guy :finger_guns:
[+] [-] ocfx|8 years ago|reply
[+] [-] swyx|8 years ago|reply
It's interesting that Lin Clark basically demoed Time Slicing a -year- ago at ReactConf: https://www.youtube.com/watch?v=ZCuYPiUIONs and in the first 10 mins of Dan's talk you see basically the same thing with user interaction applied.
createFetcher is brand new and here's the metaconversation on Twitter/Github: https://twitter.com/acdlite/status/969168681644179456 and https://github.com/facebook/react/pull/12279/files#diff-50d8...
with a couple implementations here (https://twitter.com/jamiebuilds/status/969169357094842368) and here (https://twitter.com/actualhypercrab/status/94131894239559680...)
// style discussion below
one thing I really appreciate about this talk is hard it is to even communicate asynchronous problems and the solutions to them. Personally the lagging animationFrame clock display (for time slicing) and the loading state devtools (for "react suspense") were actually the bigger "wow" moments. (Yes I know this talk is not about the tools or the charts)
he does another thing I'm a fan of - starting with a question. this helps frame the rest of the talk: explaining why the question is a big deal, breaking the question down, proposing some solutions, showing how the solutions answer the question.
bottom line - there's more to learn from this talk than just React.
[+] [-] acemarke|8 years ago|reply
Also see the summary post on the React blog for the key takeaways from the demo:
https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-...
[+] [-] Klathmon|8 years ago|reply
In my experience React has had 2 major paint points. async anything, and styling. And this fixes the former, and I'd argue that the latter isn't necessarily their job to fix.
Also, to have done this in a way that keeps a LOT of backwards compatibility is amazing.
[+] [-] jefflombardjr|8 years ago|reply
[+] [-] spicyj|8 years ago|reply
[+] [-] bsimpson|8 years ago|reply
[+] [-] sktrdie|8 years ago|reply
Interesting magic I'd say, since I wouldn't expect such a line to block my rendering.
Wouldn't it be a more "React way" to have HOC component or a render prop that would work exactly like this .read() API?
Then this HOC component doesn't need to have the IO-logic in the render, it can put it in the `componentWillMount` or inside another lifecycle method of React.What's the advantage of putting the IO operation in the render instead of inside a lifecycle method?
Also, how's race-conditioning done? Does the fetcher library have the logic to only render the last fetcher, or is it more complicated than that?
There's lots of magic, especially in the IO part, and would've loved if he went more into detail on how it's actually done - it's a JS-conf afterall.
Also, a fetch()-promise-based API is not the only type of IO-interface we have. In fact it's only one of many.
HTTP2 for instance is on the horizon offering more complex interfaces with open connections possibly returning several results like websockets - on the other hand a Promise only returns one result and then dies. Observables might be a more interesting abstraction for IO-kind of things.
Also, testing this seems rather complex since the render is effectively doing a side-effect.
In any case, cool lateral thinking and I love hearing these type of talks about cool new futuristic stuff rather than some boring old thing everybody knows about.
[+] [-] danabramov|8 years ago|reply
Yes, but only after the delay period passes. Before that, nothing will happen on the screen (at least not until I add an inline spinner as one of the last steps). Preventing a spinner from appearing and instantly disappearing on fast connections is exactly what this feature enables.
>Interesting magic I'd say, since I wouldn't expect such a line to block my rendering.
It works by throwing a Promise (and React catches it and waits for it before retrying rendering). So it’s not really magic, just an unfamiliar pattern. Conceptually this is similar to algebraic effects in languages that have them.
>Wouldn't it be a more "React way" to have HOC component or a render prop that would work exactly like this .read() API?
No. If you watch the demo closely you’ll notice we don’t show the spinner if the load is fast enough. React prevents updating the whole tree. This is impossible to express with a HOC without reimplementing the whole React in it.
>There's lots of magic, especially in the IO part, and would've loved if he went more into detail on how it's actually done - it's a JS-conf afterall.
As I said earlier the actual mechanism is quite straightforward. We throw and later React retries.
We’ll be posting an RFC with details. But for now we felt it was important to motivate this work, and even the motivation barely fit into 30 minutes.
>Observables might be a more interesting abstraction for IO-kind of things.
The core purpose of this feature is to suspend rendering until data is ready to avoid flicker and things jumping around. You can use observables in your code but I’d argue that, save for a few use cases, having components jump around as new items “come in” is a janky experience. You can always build a Promise interface that coalesces those observable updates into Promises of a final list before rendering.
>Also, testing this seems rather complex since the render is effectively doing a side-effect.
I actually think testing will get easier because now you can wrap your tree with a fake cache in tests. Then even components that do data fetching won’t attempt to reach the real network.
>Also, how's race-conditioning done? Does the fetcher library have the logic to only render the last fetcher, or is it more complicated than that?
The fetcher library doesn’t have much logic. It’s basically a cache backed by two Maps (for pending and resolved data).
React just retries rendering after Promise has resolved (or re-render happened due to another reason, like prop change). So there is no need for explicit logic handling race conditions. Instead of keeping continuations “in flight” we just abort rendering and retry when data is ready. So there’s nothing to race for.
[+] [-] aappddeevv|8 years ago|reply
[+] [-] crudbug|8 years ago|reply
I can see that being compiled to webassembly for browsers and react-native can have multiple language support ?
[+] [-] acemarke|8 years ago|reply
Lin Clark, who works for Mozilla, gave a talk on "What WebAssembly Means for React" last year ( https://www.youtube.com/watch?v=3GHJ4cbxsVQ ). In a recent Twitter thread, she said that some more useful WASM pieces are in the works, but still not likely to be a big improvement or worth the effort to rewrite React ( https://twitter.com/linclark/status/967860936550879232 ).
[+] [-] sibeliuss|8 years ago|reply
[+] [-] bajablast|8 years ago|reply
[+] [-] ty___ler|8 years ago|reply
[+] [-] alexeyraspopov|8 years ago|reply
There is a way to do this kind of things with current React version (16.2): https://github.com/alexeyraspopov/react-coroutine
You can use generators, async functions, and even more, async generators. Worth checking examples in the repo.
[+] [-] solidr53|8 years ago|reply
https://github.com/birkir/react-suspense-demo/blob/master/sr...
[+] [-] anotherfounder|8 years ago|reply
[+] [-] spicyj|8 years ago|reply
[+] [-] oyeanuj|8 years ago|reply
If so, that would solve one of the biggest pain points working with SSR + React today. Today, the solution is to have you route level component load data which is far from ideal.
[+] [-] anotherfounder|8 years ago|reply
[+] [-] hemantv|8 years ago|reply
[+] [-] acemarke|8 years ago|reply
The current version of React is 16.2 (see release notes: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-s... ). 16.3 is getting closer to release and will include several new aspects, such as revisions to the lifecycle methods and a simpler `createRef` API. The async stuff that was demoed today is likely to be in a 16.4 release later this year.
[+] [-] oblosys|8 years ago|reply