3 ms·
So if I understand it correctly, anything after this line inside the render... const movie = movieDeatailsFetch.read(props.id); ... ... will block ren
by sktrdie 9y ago
So if I understand it correctly, anything after this line inside the render...
const movie = movieDeatailsFetch.read(props.id);
...
... will block rendering and will defer to a parent <Placeholder /> component to render instead?
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?
<MovieDetailsFetcher>
{movie => <MovieDetails movie={movie} /> }
...
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 9y ago>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.
- just-boris 9y agoThank you! I appreciate all your replies, but want clarify this one: > This is impossible to express with a HOC without reimplementing the whole React in it. What is the problem here? We return `null` from the beginning, then spinner, if reached timeout and finally, the provided component, if data has arrived.
- danabramov 9y agoPlease watch my demo. It doesn’t just unmount the children when I start fetching. It keeps the previous screen in place (and fully interactive) while the new screen is loaded. If the new screen is ready fast enough, then it fully replaces the old screen without any intermediate state (empty or loading). I know it’s a bit hard to believe because none of JS libraries I’m aware of can do this yet. But that’s what it does thanks to cooperative scheduling and a double buffering-like system we implemented. I totally understand that it doesn’t quite “click” from the first explanation because you really need to try it to get it. But to explain it again: React doesn’t remove the child tree, it suspends the whole update from committing before the tree is ready. So there’s no “holes” or “spinners” if it loads fast enough. Intuitively my last example with Img component may help. There’s no a way a leaf Img component could delay the whole page from rendering before. With suspense it is possible.
- sktrdie 9y agoThanks for the answer. Since you're also the creator of Redux, how do you see this suspense feature work together with something like Redux? I imagine that Redux can continue to be used as before, but instead of using middlewares (redux-thunk) for async things, we'd use suspense instead?