React Concurrent Mode
reactjs.org
reactjs.org
I know it’s a lot to ask. But this really is a different programming model. It takes some time to see what it allows.
I spent the last 48 hours documenting it. Here are the most important two pages:
1. https://reactjs.org/docs/concurrent-mode-suspense.html
2. https://reactjs.org/docs/concurrent-mode-patterns.html (especially this one!)
It would mean a lot to me if you could go through both of them (including the examples) before forming a final opinion. Thank you.
* Suppose you want a :hover CSS selector to be applied at the same instant as a state change triggered by that hover. It's no longer reliable. Hence you're stuck with having to change the DOM in response to the state change to match a different selector.
* The browser may often set component state via its own behavior. (Small contrived ex: setting the scrollTop position of an inner element based on tabbing to a link.) Pretending that virtual DOM represents DOM, especially at X time, can get you in a lot of trouble.
How would you deal with these contrived (yet realistic) examples?
Not sure I understand the problem. Can you make a CodeSandbox demo? That said you can always wrap an update in `ReactDOM.flushSync(() => { ... })` to force it to run synchronously. But I'm not sure I understand the scenario or which problem you're referring to without a concrete example I could run.
>The browser may often set component state via its own behavior. (Small contrived ex: setting the scrollTop position of an inner element based on tabbing to a link.) Pretending that virtual DOM represents DOM, especially at X time, can get you in a lot of trouble.
I don't understand what this means. Again, a concrete demo would help ground the conversation. I think you might have some flawed assumptions about what React does, but I can't tell for sure because I don't understand what you're saying.
Thank you!
It's worth saying we've been using Concurrent Mode in production for about a year now. There are some snags we still need to solve, but they don't sound a lot like what you're describing.
The most useful thing to provide with this would be a small self contained example of the absolute core of all web programming - a form that gets data, submits it to the back end, and then displays a list of records. Nothing more. Super simple. All web development knowledge derives from this start point - for me anyway.
This is what underpins all web development and when I come into a new approach like this I always aim to implement that basic display/submit/list form workflow.
It would be SO helpful if that existed as a small standlone application.
The profile stuff is in the right direction but not it.
When there is not a basic example of that exact workflow then the cognitive load instantly skyrockets because I now need to work out how to do that and even then I don't know if I got it right because it's my guess using a technology I do not understand. So much better for the technology designers to show how to do the core of all development "the new way". Ideally with a standard form and not graphql.
React is great for Figma, Dropbox Paper, Coda, and Pagedraw: apps that are basically entirely local, and may or may not happen to be syncing with a server in the background.
CRUD is a special case of app, and the modern HTML/CSS/JS browser is pretty well tailored to handle all the needs for the client side of one. Maybe sprinkle in some Turbolinks [1] if you want some fancier page transitions.
It'll take time before someone makes a good CRUD framework on top of React. Until then, Rails is a great CRUD framework, and isn't missing much I'd expect to see React solve.
You don't really need a framework for this. Forms are incredibly easy to handle from React. A framework for declaratively caching relational data with server side components (like poachdb but for SQL server) would have been amazing though.
I don't know Turbolinks enough but it doesn't help with the connection problems I assume?
How a form would work really depends on how your data solution manages a cache. Relay has one answer for that, some other solution may have completely different answers. So I wanted to keep the first examples simpler and less opinionated.
As more libraries start to integrate Suspense I think you’ll see more such complete examples. But this release wasn’t for the end users as much — it was more for people who would create those libraries.
Hope that makes sense.
The real questions (where and for how long do you cache, what triggers are fetch, how to avoid waterfalls) are something that the library itself is better positioned to answer and figure out. We don't have a particular suggestion there (although the docs mention our recommendation).
> But this release wasn’t for the end users as much — it was more for people who would create those libraries.
Where is the documentation to create these libraries (cache integration, etc)?
Also, I'd love to read something about why throwing promises was chosen over other possibilities.
Regardless of the comparison, the features described on the “Concurrent UI Patterns” as the real deal. The “old” ways of doing things don’t allow to do any of them as expressively.
Not a big thing but also not really elegant. How would you deal with this?
I prefer to use React.useWhatever(); so I don’t have to keep going to the top of the file and importing new things.
Is my preference wrong? Much of the code that I see online does not use my preference...
> if we switch from one page to another, and none of the code or data for the next screen has loaded yet, it might be frustrating to immediately see a blank page with a loading indicator.
IDK what's the best or most accepted UX. But my gut feeling says an instant transition to the new page with light grey pulsating placeholders rectangles for the still to load contents gives the highest user satisfaction. This feels super responsive and is learned by the users for years (and it's perfectly do-able just with Suspense).
So, I wonder if the solution of clicking, seeing no change but a small loading sign or spinner next to button till the page really is transitioning is actually that more responsive/satisfying? I really don't think so, it rather makes the app feel more sluggish, reminiscent of the old days with server-rendered pages and not feeling like a actual SPA.
This is of course debatable but are there any other significant/killer use cases React Concurrent makes possible? I don't want to sound snarky but I've the feeling Concurrent is a solution with a slick name searching for a problem. I hope I am wrong.
Edit: To the pro downvoter, just try to articulate your thoughts and contribute to a discussion than pure downvoting
We’re only delaying the transition for as long as we have nothing good to show at all. When we have a decent loading state, we immediately transition to it.
I do recommend you to play with it more in a tiny project.
There is a name for this? Didn't know.
> I do recommend you to play with it more in a tiny project.
Ok, will do.
Yes, and it's described here: https://reactjs.org/docs/concurrent-mode-patterns.html#the-t...
The React team is doing amazing work!
Two questions / inputs:
1. Are the "Progressive Hydration" and "Selective Hydration" related to SSR? I could not find any info on what functionality is hiding behind the terms.
I've been wondering about how selective hydration could work with SSR.
2. In general I often find it hard to navigate the API part of the React docs - for example to find the definition of a lifecycle method (for example componentDidUpdate). There is no fast way to get to a single method.
I think this might be because the pure API docs feel a bit mixed into the rest of the documentation and therefore read more as complete articles than single packets of information.
Yea. We'll need to expand the docs on this. Progressive Hydration means React won't block the thread while hydrating HTML. Selective Hydration means you can tell React to prioritize hydrating a particular subtree (e.g. because the user interacted with it).
>2. In general I often find it hard to navigate the API part of the React docs - for example to find the definition of a lifecycle method (for example componentDidUpdate). There is no fast way to get to a single method.
I'm not sure what this means. If I type "componentDidUpdate" in top right search bar, it jumps directly to it.
With each React update however, I worry that ReactJS's scope/concerns is growing far beyond the view layer that is was initially designed to be. I worry that it will become just another bloated "framework" with unnecessary complexity and drive me into the lighter weight and more focused alternatives such as VueJS or Svelte. Perhaps the React team is getting bored?
IMO I'd like to see a base ReactJS with these advanced features being add-ons in some fashion. Keep the version I like to use lean and the complexity just where I like it.
It is. Because through experience developers realized these concerns need to be addressed, and handled... by something. It is unavoidable to have a useful application. Having the framework integrated to help you do so is much less painful than piecing together separate tools, or having 20 different libraries construct their own way to do so (if it is solveable in a uniform way that simply needs to be advocated and agreed upon).
But in any case, why can't you just not use them?
What is the pain point caused by ignoring the changes... and choosing separate tools to solve these problems if that's what you prefer?
We’d all be pissed if the sizzle selector wasn’t awesome and well thought out api.
What I keep seeing with React is really bad api design with things like hooks (because classes are so complicated, someone link that documentation post) and redux is horrific boilerplate to manage global state.
Ok no problem, don’t use it. Discourse ends there right?
No I’m sorry, reducers, hooks, ‘suspense’ stuff is not intuitive. This needs to be properly discussed.
Can I possibly even start the discussion at why the hell these things are being named in this way? Is that not a start? Functions and Classes are stupid? A debounce function that’s possibly a promise, stuff we know about, is suddenly like the ‘suspense’ api? Does any thought go into this anymore for how average developers have to approach this horseshit?
This reminds me a lot of Perl with its creative naming for things, e.g. promises giving you a `Vow` object that you can `keep()` or `break()`. Or how you `bless` an associative array into becoming an object of a certain class.
On the one hand, it is good to have precise and specific terms without reaching for a thesaurus or overloading the same term (e.g. the many meanings of `static` in C).
On the other hand, if every framework and language invents its own terms for everything under the sun, that will not help polyglots or newcomers.
(On the third hand, we have foreigners trying to spot a difference between a "promise" and a "vow".)
Promises are my favourite example of this, because just between C++, C#, JavaScript, and Perl, I can find three-and-a-half different taxonomies for the same functionality.
React used to be great, and to some degree it still is, but somehow its contributor has a vision to make it our generation's J2EE, which IMO, is due to be hated by a lot of people, including me.
Except for those of us using React Native, where functional components break live editing!
Thankfully a fix for that is coming soon, in a other 2 or 3 months maybe I can start using hooks... :/
I actually think this is pretty much exactly what the React team has done. They've made very few breaking changes over time; you can still use mixins, React.createClass, and most of the other syntax from 2015 if you really want to. You don't have to use hooks, suspense, or any of the other new features, and they've been quite explicit about their commitment to avoiding churn (because Facebook's codebase also has tons of legacy components that they don't want to rewrite either).
Or if your argument is just that you should have an option to use a React bundle that is smaller in size and excludes the new functionality -- React as a library is actually significantly smaller than it was in earlier versions [0]. They've applied a lot of learnings and improved the architecture over time, such that the framework is faster and leaner under the hood despite still supporting the same legacy APIs.
For what it's worth, I don't think the React team is bored; I think they're trying to conceive of frontend engineering as something more holistic than just the view layer. For example, old-school React is great for encapsulating and reusing bits of view as components, but how do we encapsulate and reuse bits of business logic or interactions? With hooks. How do I compose together my components to give a seamless loading experience, rather than a bunch of spinners and holes in the UI? With suspense. They're not really diverging from a view layer, just thinking at a higher level about how a view is engineered and how to provide primitives and abstractions that make it easy to build a good, complete UX (not just layout).
Thank you for this eloquent wording, you've saved me a lot of time and energy in my responses to people who don't get this point.
Overall I'd agree, but I don't think concurrent mode falls into that category. It really is a view-layer thing, and it's something that's not really feasible to do from the outside (unlike, say, state management).
No, you're just not solving problems at the scale Facebook is. You probably don't need to use Suspense.
I wonder if React would've needed to go down this path if they were built by a different company.
For example Google/Apple/Microsoft all have huge influence over browser implementations (chrome/safari/edge respectively). They might've been able to push a ECMAScript proposal for proper language support instead.
It's a very clever hack regardless but it only seems necessary because Facebook doesn't have control over the whole stack.
(it does seems like Facebook trying to fix this by building their own React-optimized javascript engine but only for react native on android right now: https://facebook.github.io/react-native/blog/2019/07/17/herm...)
I don't think this is the way it does (or should) work, though.
No company should be able to just come up with essentially an application layer pattern and immediately push that pattern down the stack, just because it's their idea and they think it's good.
A pattern needs to get a lot of practical usage, be iterated on a lot, and have strong consensus behind it before it's pushed down the stack to become a more fundamental building block in ECMAScript / browser APIs.
It's a form of make it work -> make it good -> make it fast. The process should take years. It's what happened with jQuery which eventually inspired the native DOM selector APIs, and I think the same is happening with React.
I honestly wouldn't be surprised at all to see JSX standardised as part of ECMAscript within the next 5 years. The rest of the stuff they're doing with the fibre architecture etc may follow too, eventually, if it turns out to be a really good idea after years of people using it in practice and agreeing it really ought to be part of the stack.
Part of that is because the runtime API really doesn’t change much. And for many years the only changes allowed were very tightly scoped: a single new function call, that takes two strings. Stuff like that. The attitude was “what is the smallest thing we can do to make it technically possible for to get the job done” and leave developer ergonomics to the web frameworks.
It forces people to build real platform layers on top of the browser. That causes fragmentation but it is also a hot crucible for competition. It has led to arguably the most competitive developer tool landscape of all time.
It’s easy to say “browsers should just standardize on X” and be right.
However it’s exactly the very slow acceptance of standards that had led to such a strong ecosystem of ideas. So in the aggregate, as you indicate, it’s not good to land on standards too fast.
In fact this feels so advanced stuff (React's runtime I mean) that it will probably one day land in the browser. So React's team is just one step ahead of the others, they are thinking: "what's the least amount of code we can let developers write so that we can optimize away all the rest of the stuff?".
One thing I'm worried is whether with all these amazing optimizations and very different code-execution model, we have to rethink some of the valuable simple lessons of software architecture we learned over the years. For instance, we have to rethink a bit about how to organize code and how to test it. Some things that before we could confidently say "this piece of code will run at this time" will now be a bit more like "not sure when this piece of code will run".
Cool stuff, but some tradeoffs to consider.
That cost has the same cost as other JavaScript - it's blocking, cannot be paused and has all the possibilities of de-optimizations that might occur in the JIT. The Ember team faced a similar problem in the past and I find their solution to this problem quite intriguing, even in it's obscurity. Instead of compiling their templates to JavaScript they instead compile them to opcodes in a binary format which is executed by a virtual machine.
This has several advantages. Since the template file is represented as an optimized binary the size of the file itself is incredibly small, excellent since templates can be a large part of your eventual code. Not only that but since the instructions are opcodes, this mean the VM can pause at a moment when the browser is too busy with other stuff.
Since a large part of your templates is probably static and not prone to any changes the template file also indicates which parts of the instructions are dynamic and which are static. This allows the VM to generate a separate set of instructions just for subsequent updates, only updating those parts which are dynamic.
Now I am not saying this is a silver bullet, it is however an interesting and fresh approach. In case you're interested in this check out the Glimmer site or Tom Dale's excellent talk (can't recommend this enough) on the subject:
- Tom Dale's talk: https://www.youtube.com/watch?v=nXCSloXZ-wc - The Glimmer JS site: https://glimmerjs.com/
Also really like the new <Suspense> Component! I created a {{render-on-resolve promise=foo}} Component for the app I worked on in Ember back in 2016, and we got a ton of use out of it. If you set up your data layer right, and use it well, it completely removes the need to ever manually manage a loading spinner again.
Everytime I read this (and it still seems to happens in 2019) I just don't get it--I don't get why people want to stick to the past. It's the best thing which could happen, everything is JS, FE development finally got bearable, no, enjoyable! It freed us from all the subpar patterns before.
They are overstated. You are not building Photoshop in the browser 99% of the time. Dev productivity should have a higher prio => everything in JS and nobody needs some FE VM with bytecode (wtf, I mean when we already talk about Ember, Ember interfaces are not really the fastest one despite having their own small VM).
This is what I don't get, sticking to the past and requiring stuff which doesn't make sense.
But the usual way of building things is to treat React as a total web app framework. The terminology is very confused: they still call it "rendering", even though it now usually includes data fetching, complex manipulations, event processing, etc. You just `React.render` once when your page first loads and then child components take care of the rest. It should really be called `React.start`.
> The reason for the stutter is simple: once rendering begins, it can’t be interrupted.
Rendering should never need to be interrupted. If your app is architected to have any rendering operation take long enough that it's perceptible to the user, STOP DOING THAT THING DURING RENDERING.
Want a performant web app? Apply separation of concerns properly, and limit your use of React to the view layer.
The standard React application renders a few levels down, investigates the document location and matches against routes, renders a few more levels, discovers there is some necessary data missing, fetches it, renders a few more levels down, realizes there's more data missing, stops and waits for it, etc.
This isn't a failing of React per se, but rather of how people use it.
React itself was created in opposition to arbitrary separation of concerns dogma. The concern is the component, the entity encompassing both UI and data manipulation code, as well as styling. Separating all of these parts into isolation does not result in more maintainable code or better UX.
https://www.youtube.com/watch?v=x7cQ3mrcKaY
Components have always had JSX markup along with data manipulation logic. CSS in JS and styling co-located with component extended this consolidation of cross-cutting concerns.
These, along with not manually updating DOM state as you mention, all fall under declarative aspects of React.. which concurrent rendering and Suspense are consistent with and essentially an extension of.
That's exactly what this feature is doing -- making the view layer more flexible in how it responds to the data received.
Aside from keeping rendering lightweight and never needing to block, this architecture also:
1.) Lets the app batch up RPCs to minimize network traffic.
2.) Lets you easily swap out the network layer for mock data so you can develop the UI before the server is ready (often very important because backend dev tends to be slower than frontend).
3.) Lets you handle failures sensibly. Oftentimes a failed RPC requires that you make non-local changes to the UI (eg. display a butterbar, change page layout to not display a component, display an alternate component).
4.) Lets you easily add logging, tracing, and performance profiling capability to your network layer.
5.) Keeps your UI components reusable so they aren't coupled to a specific app or backend.
6.) Optimally handles cases where data (derived) from the fetch shows up in multiple places in the UI. One classic example of this is a table where the total count of results is in a UI element elsewhere on the page; another is when logging in changes the elements you're shown.
Requires a lot of coordination code, duplication, and coupling of your entire component hierarchy to each other no?
Still doesn't solve disparities in data loading completion across different services. Render giant loading spinner or the entire app?
Plus, you will be overfetching if everything is pulled into top level components.
For exactly the reason componentizing the markup was a good idea, it is beneficial to breakup data loading and manipulation concerns across components, while also being able to reuse some of this logic through abstractions (hooks).
>Keeps your UI components reusable so they aren't coupled to a specific app or backend.
They are coupled to each other by virtue of where you define them in the hierarchy and which props they need to pass to and receive from each other.
Components are highly reusable.
Containers are coupled to specific API calls or logic.
I still default to doing as much data fetching on the top-level page component as possible and trickling data down. But if there's certain logic or data fetching I re-use in multiple places that don't need to block the whole page, I'll definitely refactor that into hooks + containers and delegate that data fetching lower in the tree.
These added features are simply extending that paradigm in a natural way that supports better UX, within the constraints of the javascript language.
Nobody says they are easy problems to solve, but it's sort of a non-sequitur to argue alternatives that don't solve them (or don't absolve one of having to solve them still.)
Only if we tunnel-vision to a narrow definition of "idiomatic patterns in React". In Angular, for example, this is solved via a DI system. In React, you actually get much higher level of coupling because all orchestration-type things need to go through context providers (meaning a given component may only work if certain providers are installed at the top level of the React tree). In Angular, a coupling mismatch issue would throw an error saying X component requires Y dependency and the solution would be to simply inject what is missing. No need to go edit your layout file or whatever.
The coupling I'm mainly referring to is within your own app hierarchy. Moving/adding components around within your app requires editing the long chain of component hierarchies and data dependencies.
Yes injection works to solve, but isn't this similar to hooks (encapsulating reusable logic/data fetching)? Whether a top level Context provider (that will also throw an error if not provided) or injection.. is there much of a difference? And does this not seem less coupled than prop drilling through the entire component chain?
In the context of a discussion about UX, loading states, spinners, concurrent rendering though, does Angular address this? I'm not that familiar with it..
It's basically the Law of Demeter applied to UI components, and has both the benefits and the pitfalls of the LoD:
https://en.wikipedia.org/wiki/Law_of_Demeter
Boilerplate code goes up, method signatures get duplicated more (though JS has easy varargs & spread operators, so this can be minimized), method logic is less duplicated, and coupling is decreased.
> Still doesn't solve disparities in data loading completion across different services. Render giant loading spinner or the entire app?
You have a choice depending on product concerns. You can render a single loading spinner for the app. You can render individual spinners for different loading components (just pass the 'loading' prop into each of them separately), but have them all resolve at the same time. Or you can issue separate RPCs for each, as if the component had made the request itself, and have each component re-render as the request comes back.
The nice thing is that this choice is not embedded within the component itself, so you could for example start out by rendering a single global spinner until a single global RPC comes back, and then as you build out the backend and separate calls into different RPCs, change component rendering to incrementally show the new data.
> Plus, you will be overfetching if everything is pulled into top level components.
Again, this is a product tradeoff. Do multiple components display data that changes when an event happens on one component? It makes sense to batch up the RPCs then, because they'll always be triggered together anyway. Or is each component displaying data that is manipulated only by that component? Then give it its own RPC, but you should still kick it off at the top-level and delegate up the tree so that you can change this behavior easily.
In my experience, UIs designed by frontend engineers tend to have lots of RPCs that are both kicked off by and only affect a single component, while UIs designed by UI designers and CEOs (and users themselves) tend to have a lot of non-local changes throughout the UI in response to an event in one component. This is a source of tension between these groups, but only one side is signing the paychecks.
With the component paradigm, these things can't always easily be separated. Or, it is useful to consolidate data concerns and UI concerns, but often data concerns like UI have a hierarchy, or the concerns are spread all over the place (not in neat, clear groups).
It is very useful to declaratively specify the recipe for both UI and data, and allow the framework to coordinate these things across and within components.
>Rendering should never need to be interrupted.
Following this line of thought would lead to not rendering anything until everything is ready. Large applications have multiple levels of components and data concerns. These solutions allow for isolating responsibilities at smaller levels, and coordinating their presentation in ways that produce good UI (few loading spinners, flickering etc).
>and if limited to that use
This simply passes the responsibility somewhere else no? And any significant apps can't avoid these concerns. You'd still be left with the coordination between React and whatever else was handling these concerns, but with much more code likely to tie them together.
But yes, rendering doesn't need to be interrupted. They specifically mention architecture decisions to avoid rendering operations that take too long (debounce and throttle).
This eliminates previously necessary architecture decisions while providing a better user experience. How is that solving the wrong problem?
Also, this is still the view layer. There's nothing React-specific about data fetching, "complex manipulations" or "event processing".
Previously if you had a text input whose output you used to fetch some data and render in a list, you'd use debounce.
Now, with Concurrent Mode, you don't have to and can instead use the React API.
The React model makes sense because of how state management is part of it, not strictly just the view. I think seeing it just as a view library necessarily leads to the rest of your other conclusions. I recommend reading https://overreacted.io/react-as-a-ui-runtime/ by Abramov which describes React as a UI runtime.
Embracing React as a UI runtime you eventually find problems that concurrent mode solves. Some are related to performance and concurrency, but some are related to managing state and data dependencies. It also provides idiomatic ways that encourage good UX (like managing error/loading conditions once, at the right level).
Seperation of concerns is all good and well, but there are some product experiences you simply can't do with plain HTML.
At my workplace we frequently encounter cases where the rendering itself - just the evaluation of the pure React functions - takes hundreds of milliseconds, if not several seconds. This is after memoizing every part of it that can be memoized, after profiling it for places where work can be shaved off, etc. Sometimes there really is just that much rendering, and unlike other types of computation, today's React gives you no options for putting that in a worker or breaking it up. Tabs' single threads mean you can't even scroll in the interim. Concurrent Mode is the only hope we have of solving that case, other than avoiding it with paging, which is what we currently do.
Isn't that React Fiber? [1]
"Fiber breaks down animation into segments that can be spread out over multiple frames."
How does that differ from React Concurrent? Are they compatible?
Confusingly, there are multiple tiers of "updates" happening in a React app. One is when your render functions are called to recompute their React element trees. Work can be avoided here via shouldComponentUpdate(), etc.
Once React has this internal data structure, it uses it to do another level of updates to the actual DOM. This part is totally black-boxed from the application code, because React has all the information it needs internally to avoid unnecessary updates at this level.
I think, maybe, that Fiber is specifically focused on this latter stage. Concurrent Mode would then be the stage 1 equivalent of what Fiber does in stage 2. This would be why it has an effect on how application code can actually be written, where Fiber didn't really.
Fiber does specifically divide the overall update process into two general phases. "Render phase" involves walking the component tree, asking components what the desired UI should look like, and calculating the diff and necessary changes based on the previous requested output. Once that's been calculated, it is applied all at once in the "commit phase".
Concurrent Mode is, loosely, additional modifications to that rendering logic to enable pausing partway through the "render phase" process, tracking one or more in-progress versions of render passes, prioritizing them based on the source of the requested update (user input vs network request, etc), and recalculating in-progress renders if another higher-priority render was pushed to the front and committed to the DOM first.
I really recommend browsing the react reconciler source code. I don't immediately understand most of it, but you can get the gist of it.
You can combine this with virtualization if you have lots of non-visible components.
The disconnect here is the definition of "concerns."
One analogy is, imagine cutting a square into stripes. You're implying that there's only one way to do so, say, cutting horizontally. But you can easily cut vertically and still end up with well separated stripes.
The stripes, whether horizontal or vertical, are separated cleanly.
While you may see splitting by data fetching, view, etc. as the only way to separate by concerns, there are certainly others. The component model that has been adopted by many React users is one such way.
At the top level, we slice our projects vertically (by feature) at work. But it doesn't mean we have 1 giant god framework that handles all layers for a given feature. We still segment by layer. And we still have layer focused libraries.
As is tradition, frontend developers are still slowly reinventing the techniques from other fields.
The neat thing about UI is that your end user is a human and a human can't visually process too many items at once on a screen. This means you should never really need to consider more than O(thousands) elements to render either. That should always fit under 16ms.
This has been known to game developers since forever. Don't spend resources on things that won't be displayed. If you are doing too much work that means you aren't culling invisible objects or approximating things at a lower level of detail.
In practice for frontend developers, this just means using stuff like react-virtualized so that even if you have a list of a bajillion objects, only the stuff in the viewport is rendered.
I am just trying to support the grandparent comment that the current optimization is at the wrong level of abstraction. React is at the level of a "scene graph" (dom). If it's slow, the first technique to reach for should be stuff like "occlusion culling".
Their documentation explicitly states that Suspense is not for data fetching. That's what the new Relay APIs are for.
https://reactjs.org/docs/concurrent-mode-suspense.html#what-...
So I don’t get it. You can’t interrupt the browsers native paint cycle. Once you say node.appendChild or change some attr/style. The browsers gonna do what the browsers gonna do.
React was simple. With hooks , concurrent mode, virtual events and mumbo jumbo. I don’t get it anymore. It’s not a lean mean library that takes minutes to run and one can’t read the source in a couple of hours
I feel that.
One of the top voted questions was "how do I do a simple form submission?" and the response was "it all depends on your state management caching behavior". I think that highlights just how beginner unfriendly some of this stuff has become.
I'm a fan of the Material-UI project which provides the visual components and ability to style them however you want. React Final Form is terrific with managing forms (and easily made very performant).
imho, these form components become really nice to work with...
Demo: https://lookfirst.github.io/mui-rff/
Codesandbox: https://codesandbox.io/s/react-final-form-material-ui-exampl...
What's the alternative? In Angular, the whole problem would've been solved with their template engine + built-in forms module. But Angular has a huge API surface.
With React, you have your template engine. Form state can be managed with a third-party library, like final form. It's no broader of an API Surface than Angular would be.
So the question is, when were these things ever "beginner friendly"? Were forms easier 5, 10 years ago than they are to implement today?
Yes. Less was expected of us. Things have grown and evolved in such a way that user interfaces can provide much better experiences than they use to be able to and as the capabilities have grown, so have the expectations. Along with it the barrier to entry has gone up.
Yes, so that's why DOM manipulation is slow. And this change basically allows you to not do DOM manipulation on every tick.
> The browsers gonna do what the browsers gonna do.
The browsers gonna do what the JS code tells what the browsers should do.
> React was simple.
And that was insufficient.
> I don’t get it anymore.
Then you should be looking at hypersrcipt or preact or something else. Nobody is forcing React to you.
I'm excited to start learning more about Concurrent Mode and Suspense.
It's akin to doing `Header( LoginLink( GetLoginName() ) )` versus
``` Header(); check_for_interrupt(); LoginLink(); check_for_interrupt(); ... ```
The interrupt between rendering components (pieces of work) can now cancel the whole render operation (because say, the parent component has now been updated / wants to be re-rendered), or can pause the render tree while something like a high-priority animation frame is rendered.
Really though, you’re over focusing on the CPU part. The IO part is much more interesting.
(And you don’t throw a promise, you return and reject it)
The issue at hand is fully synchronous render functions that, e.g., go through a giant list of objects and call a bunch of other render functions on each one synchronously.
This change allows that one function to finish, and interaction to continue before all the children render.
You mean returning unresolved promises? Or returning rejected promises? Or just throwing an error?
React will catch promises thrown during rendering, and treat that as a request to "suspend" the component and come back to it later once the promise has resolved.
But version control is a lot more approachable.
That clock animation is pure genius:
1. https://reactjs.org/docs/concurrent-mode-suspense.html
2. https://reactjs.org/docs/concurrent-mode-patterns.html
I spent the last 48 hours writing them, and I think they answer your questions and contain many, many demos.
Does having an external state tracking system like Redux combined with a tracking a "loading state" solve the in-flight rendering item? My initial take is that this allows for a simplified model where data is loaded directly in components without the wrapping dance for tracking client load status.
What is there to render but loading states when there's no data?
const myApp = () => (
<Header />
<Body />
<Footer />
);Body can make the network request, and store the result in state. Since the network request took 200ms, Header and Footer have already rendered.
Everything else, or nothing prior to some threshold, or what you specify and where within the Suspense abstractions. Since you don't know exactly when all of the data is ready in different parts across time, manually coordinating all of it without a cohesive declarative paradigm results in more code and more errors, and more bad UX of flashing spinners and flickering loading states.
The point of concurrent rendering and Suspense are to abstract at a higher lever, or more general terms what to do and what to render when there's no data.
I know I sound like a broken record, but these two deep dives answer all these questions:
1. https://reactjs.org/docs/concurrent-mode-suspense.html
2. https://reactjs.org/docs/concurrent-mode-patterns.html
I hope they’re helpful.
Sometimes you need a more complicated solution for a complicated set of requirements.
Do you need GraphQL? Nope!
Do you need GraphQL for an application that needs to fetch a lot of highly relational data with nested objects? No, but it sure would be more optimal to just ask for what you need in one request in the shape you expect, instead of either:
a) Multiple REST API calls that you need to combine and coordinate in the frontend
b) A specialized REST API call that gives you all the data you need in the shape you want, but it isn't very RESTful
c) A REST API call with a bunch of `with[]` params (a la Laravel) that indicate what nested objects you'd like, but not their exact shape
So, which is more maintainable then?
—
The other wild misconception I see time and time again is this:
Of the five things you mentioned "React/Redux/Graphql/hooks/bazillion-libs", only the first one is React, and the second-to-last (Hooks) of which it is optional.
The rest are dependencies you opt for, hopefully after weighing pros and cons. You don't need to use Redux, GraphQL, react-table, whatever other dependency in your React web application.
Please separate out React from React-based codebases from code architecture. They're not one in the same.
I don't think bundling React the view library, Redux the state management library, Typescript the Javascript superset, GraphQL the API query language, Webpack the bundler as one "React the platform" is helpful.
I'd like to see the developers that automatically merge all of these technologies together by default because everyone else does it (or worse, because X Big Tech Co does it) to start questioning and exploring other options. I'm not sure how to get there, and maybe my comment above is my contribution towards convincing readers to reconsider their understanding of the React ecosystem.
You're right though, it is a mess. I think the mess comes from people and how they code, not so much this specific library nor the language.
React's trying to add functionality & developer workflow on top of an existing UI system (document rendering system...) that wasn't meant for application UI, has barely been modified to accommodate it since using it for that became normal, and isn't doing React many favors, and for bonus LULZ it's doing that in a single-threaded scripting language that somehow manages to have both too many and too few features to capably support React's vision, with the result that they're constantly fighting & replacing all the technology they're working with.
This puts it at a significant disadvantage versus any half-decent native UI toolkit, when it comes to complexity.
But that isn't the world that is exposed to web developers, so a lot of complexity is necessary to take a declarative rendering language and trick it into giving developers the kind of control they need to address issues like the naive solution for altering the declarative tree and triggering a repaint not necessarily cohering with what users consider "good looking."
Android is dead simple to develop GUIs on. Because it was designed for that. .NET is dead simple to develop Windows apps on. XCode/Swift is dead simple to develop Apple apps on.
Websites should go back to being advertisements for the the native apps. Here is a billion dollar company doing just that:
I guarantee you that the next Facebook, whatever it is, will not have a web client.
If a user input or other render - triggering event occurs partway through a render pass, React can set aside its partially completed calculations, start a new render pass, complete that and commit it, then effectively "rebase" the original changes on top of the just-applied change.
Shawn Wang has been collecting links to all the info on Concurrent React that's been released over the last couple years :
It doesn't make sense to try to summarize that page. I've been writing it for two days and it's the shortest that I could compress the essence. I'd be happy to respond to questions you'll have after reading :-)
If react does a better job at speedily handling user input to the point of seamless interaction, then I suppose that would be good. What are we losing from choosing to make a layer itself responsible for the speed of returning output (rendering) from input (user interactions)?
But with React it has to reconciliate the input field.
I mean just rendering a ton of stuff my browser will hang up on straight html page too. Granted that's a very general example and Concurrent is solving some things that in some cases aren't quite 1:1.
I know all those frameworks exist to solve a specific problem, but while doing so we are creating hundreds of problems that never existed before.
Namely, developing something once and having it work ideally everywhere on the planet (slow network vs. fast network).
They're worried about shaving kilobytes here and there - concurrent mode helps to dynamically render/load items in an order that makes sense for people on slow networks where it wouldn't even matter for people on fast networks.
Namely, developing something once and having it work ideally everywhere on the planet (slow network vs. fast network).
Providing users with the best experience possible in their browser regardless of the limitations of their computer and network is a problem every front end developer should be trying to solve.
Very few of us work at the scale of Facebook, but that particular problem isn't a scaling issue.
Suggesting we can ignore the problem of a bad experience for users on slow networks just because we operate at a smaller scale is plain wrong, and anyone making web tech with that attitude is failing the people who have to use the code they write.
You mean like being able to have juniors up and running and writing legible and maintainable (if at the cost of being opinionated) code?
Mainly because I've only ever worked in comparatively small companies (fewer than 30 engineers) and every step of the way React has uncannily been solving the problems I've been facing (in fact, my decision to try React came at the end of months of trying alternative solutions to view layers until deciding to bite the bullet and try that weird JSX thing).
For me it comes down to a simple question: Is what you're doing sufficiently app-y to justify an up front framework cost of around 70kb (React + your ecosystem libraries of choice)?
If the answer is "no", there's a follow-up question of "is that likely to change within a year?"
It's relatively frustrating that React is one of the largest React-like frameworks (e.g. compared to Preact and Vue), but they've mostly been focused on different problems for the last few years. I'd expect React to get smaller and faster once there's less of a focus on fundamentally changing and simplifying the way we approach certain types of problems (e.g. the ones that Suspense solves).
Er this ended up being longer than expected, sorry about that!
Part of it I think is that we're still in a period of growing-pains. The web is transitioning from a document platform to an app platform, and until it fully catches up to that mandate, we have to bridge the gap with layers upon layers of frameworks. I think we're entering the downslope of that process, but it isn't finished.
That said, I do wonder why there isn't so much as a proposal for native HTML templating (the <template> tag doesn't count). This has been industry-standard practice for 5 years now and would benefit immensely from a native implementation. It seems like it's well past time to get that ball rolling.
There were XHTML with XLSTs. It was widely rejected, most people didn't even see the point.
function myComponent(props) {
return `
<div>
${props.name}
</div>
`
}
...
document.body.innerHTML = myComponent({ name: "Bob" });
The problem is that it always completely obliterates the DOM, instead of changing just the parts that need to change. The browser, of course, has plenty of information with which to do that in C++ instead of requiring the use of something like React. But for some reason it hasn't been implemented.I'm of the opinion that sticking with React when Lithtml exists is just fear of writing vanilla classes.
About ease of use in programs, if you were using XSLT, you had to output XML which then had to be put through an XSLT processor. If you are outputting XML to another program or database or whatever anyway, that would be possible. But if you are only outputting XML so it can be transformed to XHTML by an XSLT processor, for which you had to write something in XSLT, you could just spare all the hassle and directly output HTML anyway.
FF and IE had XSLT processors in 2006 and I suppose they still do (although could be wrong about the still do), there were at any rate XSLT processors in browsers back til the early years of this century, although hey, not in every browser. Opera was definitely dead set against ever implementing one.
You did not have to output XML, you could output XML, text, or html, depending on the output format the processor would handle how your output format should work (as it was a declarative language) thus if you output html the processor would handle turning XML like br elements into html br elements, not that that is any great thing, but it is a fact you could output other things than XML
The saddest part is that the shortcoming that the initial framework tries to solve is, most of the times, insignificant compared to the complexity that the framework is adding.
The truth is that we are all just "play-testers". We keep mocking new browser features, put them in the real world, test them, and then browsers can easily decide what features really provide value and should be implemented.
The real problem is that this is a never-ending cycle. We started to like this creation of new features so much that we never really focus on what we have and try to make the most out of it. We keep adding bloat to fix imaginary problems, when we already have all the tools that we need. I am not referring to new functionality, such as Web Payments or OffscreenCanvas, but to frameworks that try to mimic what the browsers are doing and just do it in a slightly better way.
That's where I disagree. I think the web was fundamentally unsuited to the "application" use-case and has been making its way in that direction for 5-10 years now. I think once we reach that destination, the churn will drop off dramatically. The platform will become enough, for the most part, and we may shrug off the need for comprehensive "frameworks" altogether in favor of simple libraries, like most other language ecosystems.
It clearly hasn't though, as demonstrated by this very OP ;)
Everything people are complaining about here, in addition to the problem being addressed by Concurrency Mode, are things that writers of native apps don't have to fight with:
- Lack of concurrency
- Complex, layered build systems
- Several layers of syntax transformation as an industry baseline
- Lack of a consistent story for modules
- Overall jank-proneness
- Platform bloat like that in Electron
But then why re-solve all these problems if you can just build a native app? Because native platforms over the decades have repeatedly tried and failed to a) be truly cross-platform and b) have truly flexible, well-documented layout systems. The web gives us those. We just have to go back in and add the things it lacks.
When all of these problems have been solved and a new framework is still coming out every week, then we can talk about frivolous effort being expended. Until then these are real, worthwhile problems being worked on.
I miss the old internet some days.
Yeah, web is not only about documents these days (how it was originally intended) but it is also about running and hosting real-time applications.
But I hear you, people indeed use wrong technology to solve their problems. I agree that there is no need to use any React and Angular to display a document.
Oof I do not miss that! You are 100% correct that tool selection is a major decision in how well a product, app, or website responds. Perhaps we need more of that then?
At the extremes, you either end up with thousands of lines of spaghetti code or an over-engineered mess serving up a html page with a single image on it.
Web frameworks are just a tool that needs applied to the right job.
Really? I think its pretty obvious that the complexity of websites (or really, web apps) is increasing every day. For the most part, web sites are not just static content any more. More devices, more users, more features for even the most basic types of sites lead to increased complexity of building something that meets those demands.
>We have build thousands of front-end frameworks and all of them try to solve the same issues that should have been solved by the browsers themselves a long time ago.
Sure, but they haven't been solved so do we just say to users "Nope, sorry, Chrome never implemented this so it's not possible"?
I can't help but think that this type of argument is based on an idealized version of the web where every website can be reduced a personal blog like the "good old days". The web has evolved, and so have users and what they expect from it.
I mean sure, if you are literally creating a personal blog - don't use React. But otherwise, I don't really get this POV.
I would instead argue that websites today tend to have far more features and functionality than their counterparts of 15 years ago.
I found it extremely easy to jump back into and high productive. I feel like the next generation will rediscover the simple nature of include a file.
People are doing things with websites they never did before. Office applictions, games, native-like UIs, real-time messaging, analytics and dashboarding, programming.
Good or bad, websites today have different interactions, behaviors, and development costs than 10 years ago. It sneaks up on you but if you see screenshots or use an old website you will immediately notice the difference.
> but while doing so we are creating hundreds of problems that never existed before.
Such is innovation.
> Tens of years of "evolution" and we just keep making everything slower and more complex for no reason, apart from being bored or wanting to be trendy.
Try making a website good enough that people will actually pay for it.
You may find your opinion changes.
Don't get me wrong, I actually used and love(d) React, but it feels like in the majority of cases the library is misued and people just create normal sites (that could have existed 15 years ago, but a loat bloatier) instead of creating the apps you were referring to.
So React shouldn't get new, powerful features because some people might (ab)use them for simple websites that don't need it? That's bollocks and I'm sure you can see that. If you are generally annoyed about this, go rant on Twitter - no reason to do it here.
I love HN as well, but this is a niche site for niche purpose.
Complexity of building dynamic documents is actually going down. If you are to build a typical web site from 200x using modern browsers, you wont need jquery or anything like that. Networking, security - everything became 100 times easier and faster.
But if you want to build a desktop application using DOM as a rendering layer, then it is a different story.
If I can't just include a file things are going to get more complex.
State machines are a method to solve a problem that could be expressed in other ways to make it fit the language better.
If I want to implement a rich text editor in my website, it will be as easy as 'npm install ...' and an import statement in the relevant file.
Is there an advantage to make a state machine to solve this?
But the tooling and npm packages are all optional. You can import React from a CDN and use stuff like htm [1] or domz [2] instead of a JSX compilation step.
Or you can use Vue instead of React (their tutorial starts by using Vue via CDN).
If you're able to only support 95% of the browsers, you can use most modern ES6 features, including ES Modules and async/await.
If you still need the tooling, there are options like Parcel [3] or Poi.js [4] that require zero configuration. You don't really need the complexity of Webpack.
--
[1] https://www.npmjs.com/package/htm
[2] https://www.npmjs.com/package/domz
When I tried to include other packages they were only available through npm (some had cdn versions but most did not). You don't get access to the entire ecosystem without the tooling.
Either
A) You are ignoring that modern web apps are very complex w/ dashboards and complex flows that can't be modelled with a simple URL scheme.
B) You are saying "Actually I don't have a problem with React, my issue is with people using it everywhere for simple things". Which is silly because
1. That's not relevant to the article. Go talk about that issue somewhere else.
2. This feature is clearly only meant for more complex React applications, so you don't need to worry about it "over-complicating" simple React apps.
3. This feature would be just as hard to implement in a traditional web-app. JQuery based web-apps aren't simple, they are imperative hellholes.
4. React makes "simple" web-apps simpler. Even for websites that don't need React, using it can make the dev process easier.
...the problem here is developers consistently try and shape the round hole to fit their square peg. Web apps are not the appropriate product for this design problem - the solutions are desktop applications.
It's very unfortunate that the ecosystem is so hostile to traditional desktop apps and solutions, but that gets back to OP's point - these frameworks are solving problems we've manufactured.
So if by ecosystem you mean money, yeah, but I only make web apps nowadays unless someone comes with a big bucket of money for the desktop app beforehand - which I never see happening for some reason.
But if your product is not just about information retrieval or collection, rather based on things like complex user interaction, persistent local data storage, integration with devices on the users machine or complex interactions that need to override mouse/keystroke behavior... then I'd question whether or not it should be a webapp.
Discord is a really good example of this to me. They have a web client, sure, but the app itself makes sense on the desktop. There's nothing wrong with using web technologies to build the app, but the mental model of how browsers send/receive data from a server somewhere seems fundamentally incompatible with how such an app should work.
What I meant by hostile is that publishing apps sucks for a lot of reasons, from lack of user demand to platform issues, poor sandboxing, distribution issues, etc. It's a shame, because that's how we wind up with such crazy complexity in web apps, when we could just stick to native OS APIs and call it a day.
So the solution would be to exclusively some "web technologies" (HTTPS, WS) to deliver content and messages to the application? I guess by creating a bunch of clients, managing request states and working around the intricacies involved there (or in the libraries you used).
So after that's done then lets add some kind of auto-updating mechanism. After a while you get it working, awesome. Lets hope users constantly keep it updated! Spoiler: they will not.
And then, when you think you're all finished, someone from product comes in and says "hey we want to A/B test this feature on these specific clients, when it's a Tuesday and they are connecting from India. Oh and these requirements will change every 24 hours". Great.
"If only there was some set of technologies that made all of these problems convenient and effective", you think to yourself as you check Slack.
Regardless of where the problem came from, we still have to solve it.
EDIT: This is better than what I've implemented. My solutions have involved data-load waterfalls. I'll have to crack the code open and see how Suspense is pulling this approach off; it's pretty cool!
Yes please! I do hope this pattern catches on. Not only are full page loading screens on every interaction obnoxious, I’m sure they also require an epilepsy warning.
The challenge isn’t the low level mechanism. It’s how to make it composable so people can build their own abstractions.
But reading this I think there is a bit more to it and I would assume for some things to work you have to actually try rerendering on some scheduler not just wait for promises to resolve (like they mention in how multiple Suspense components are aligned and rendered together if they resolve in close succession)
The way we currently solve these problems, which for some reason didn't make it into the examples, is with Redux:
fetchProfileData()
const ProfilePage = connect((state) => ({ user: state.user, posts: state.posts})(ProfilePage)
In truth, we'd also track a loading and error state as well. This solution is not subject to data races or waterfalls.One could argue that their new implementation provides a better interface... but I'm not so sure. The final ProfileDetails won't work outside a Suspend because the Suspense API is Throwing Promises [1]. In fact, it crashes the app :-/
Note how we eliminated the if (...) “is loading” checks from our components. This doesn’t only remove boilerplate code, but it also simplifies making quick design changes.
Yeah, I did note that, but is it a good thing?! Rendering in React is currently localized to the render function of the Component itself! Its rather easy to miss the function call that is throwing a promise during rendering to cause itself to not be rendered! Suspense is a hidden interface to delegate rendering to a parent - perhaps an even more error prone Context API.All in all, this feels like Hooks to me - the trivial examples in the Docs seem perfectly cromulent, but in practice, the ergonomics are so bad that any time I try to use them, I am forced to spend several hours debugging stuff because they lose predictability when composed. Perhaps this won't be so bad in practice, but I'm having a hard time imagining how to compose components that interact with Suspense. Of course, I'd love to be wrong (about the API) or proven wrong and everything turns out to be magical. Maybe TS makes it perfectly obvious in practice, but maybe it generates a ream of gibberish instead.
The true sleight of hand in most of the examples is to kick off `fetch`ing before a component gets mounted that needs the data:
// Kick off fetching as early as possible
const promise = fetchProfileData();
function ProfilePage() {}...
As far as I can reason, there is no good way to do this. The data dependency graph is naturally defined by the component graph: after all, components know what data they need to render, but that graph can't be resolved until after they are actually rendered. So, you end up defining it in two places with an implicit coupling or via HOC or get a waterfall. I'd be open to some magic here.1. https://codesandbox.io/s/frosty-hermann-bztrp - see fakeAPI.js (couldn't figure out how to link to it.
As the author of Redux I can assure that it’s not designed for — or capable of — providing the features described on this page:
https://reactjs.org/docs/concurrent-mode-patterns.html
Hope that makes sense.
Regarding fetching, as the page says, in practice we use Relay for that. If you want to look at how it works in production, the page does link to Relay docs.
burnnnnnn
Sure! Seems like we are talking past each other (these problems being waterfalls and data races and the examples coming from https://reactjs.org/docs/concurrent-mode-suspense.html).
If I were to write this comparison, the gist of it would be "Yes, you can solve waterfalls and races, but what's your answer to features on this page: https://reactjs.org/docs/concurrent-mode-patterns.html"
Redux is simply a synchronous external data store. It does nothing in regards to how you fetch your data. While React-Redux does trigger UI re-renders when the store is updated, we rely on React's own scheduling capabilities to handle actually updating the UI. So, no, Redux definitely does not solve the patterns described in those docs.
I'm hopeful that we may be able to eventually build some integrations between Redux and Suspense at some point in the future, but frankly we've been waiting for actual docs and examples of how all this stuff is _meant_ to work. Conveniently, we now actually have some (thanks Dan!), but it's still going to take a long time to absorb what the APIs are and what techniques may be possible.
EDIT: JS is already dynamic. If a function returns a promise then it should be awaited and render blank or nothing in the vdom. This is obvious, except in JS where architecture prefers synchronous-only design with async being an afterthought.
It's the general state of Javascript and frontend code which is finally starting to understand what multithreading and concurrency is.
It's surprising that JS has so much history with callbacks and events but async methods are unsupported or require workarounds by so many core frameworks and libraries which default to synchronous APIs for everything.
So what if React is massively bloated, stupid syntax, can't use regular HTML, can't use regular CSS, can't use async/await, have to do everything a special way...literally, SO WHAT. It IS REACT! OMG behold it's glory. It came from FB, maybe if we use it we can scale like FB!
Omg the idiocy. But keep on slavishly following the next front-end dev trend. I can't tell whether front-end dev culture is a massive circle-jerk or cargo cult. And don't mention the irrational hate attempted to be heaped onto anyone who dares question the programmatic thinking of the official line of the React Appreciation Society.
Why not just ask people why they use X? Plenty of veteran developers on HN could tell you why they like React despite a sea of alternatives, but it's easier to just cast aspersions instead of understanding.
It's a shame to see this on HN which is supposedly a community of craftspeople who build things. If you can only come up with insults for why another group of people do something, it's time for you to get up off your ass and ask someone in that group. And this extends beyond just engineering, it's how you understand other people in general.
I'm pretty sure I'm being baited here but all of these are categorically false.
React is the first framework that allows me quickly build rich web-apps that load fast, perform fast, and have a structure and order to them that keeps them enjoyable to work on as they get bigger.
I have no loyalty to Facebook and I've tried pretty much every major framework or library that's gained traction in the past 15 years.
It's too low-level and hard to manage (if you care about correctness and performance). You eventually have to invent your own locking (to enforce ordering guarantees between read/update remote calls) and do resource management by hand (async task becomes something you have to keep track of, in case you need to abort/restart it, recover from network errors, etc.)
There are much better abstractions for getting remote data. State machines are one such abstraction that I like to use.
I'm still awaiting the day when web devs rediscover that coding an event driven FSM with pure functions is acctually the most flexible and easiest to reason about way to manage client-server communication. Meanwhile it's my secret sauce.
There's no reason why these frameworks have to treat async functions as a special case. If the promise hasn't resolved then don't render that node. The fact that it was ever blocking is just a byproduct of sync-only thinking. This is extremely common in JS where libraries are designed around sync first and have workarounds to support async.
You get a reference to it and it's a resource to manage manually. You need to ensure that the side effect you attached to it at one time will not mess up anything in the future, despite not knowing in what situatuion in the future it will be resolving. User might have done many other operations with tha app in the meantime, etc.
You may also want to carry around an AbortController, in case you may need to cancel the operation behind the promise (like a fetch).
So promises are just references to some resources.
Async functions actually make the management of async jobs harder, since you're basically encoding a sequence of jobs into the structure of the functions you define and you don't even see the promises behind those, and you'll not have control over their execution from the outside, unless you explicitly code for it at every point.
You don't get to say: ok, whatever's going on now with all my data fetching async functions that may be currently running, I want to pause them in whatever state they may be now. Or cancel them right now, or restart them from where I paused them.
With explicitely coded FSM, you can do any of this, easily.
Hope this is helpful.
Wouldn't making the lifecycle methods async not fix this?
If the entire runtime was async then wouldn't that naturally allow any other user/component level async calls to naturally work as expected?
In some way, Suspense is "making them async" btw. Or, rather, making render async. So maybe we're talking about the same thing.
If the React runtime was completely async (as in returning promises for every function) from the `ReactDom.Render()` to the lifecycle methods to render(), then user code can transparently support any `await fetchSomeData()` calls. Basically change every method from:
f(x) => T;
into: async f(x) => Promise<T>;
I believe the major change would be the runtime checking if the render() promise was resolved or not, and skipping the component if it wasn't. Am I completely off base here?So none of this could be abstracted right? Like, jesus, not functions but reducers, not straight for ‘cancelRender’ but ‘Suspense’ Api.
There was a guy once upon a time that said ‘$(‘.comments’)’, that makes sense , it’s intuitive. These APIs are just getting dumb from a common sense perspective.
I’m just showing you that a shitty version of discourse is available if you need it for your use case.