Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
React Concurrent Mode (reactjs.org)
446 points by gmaster1440 on Oct 24, 2019 | hide | past | favorite | 294 comments


While this page went to the top, I see a lot of comments that show people haven’t seen the other pages.

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.


Thanks to Dan and the React team - great work as always.

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.


It’s a bit tricky because as the docs explain, the only Suspense integration we currently use in production is Relay. I definitely agree we need a complete Relay example that people can run (maybe Relay docs have one already?). But a lot of people don’t know Relay so it would be hard to teach both new UI patterns like useTransition and Relay at the same time. This is why we didn’t do it in the main docs.

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.


Maybe I just missed this, but I couldn't find any kind of guidance for library maintainers on implementing Suspense. Reading the demo code + React-Cache it seems that it's basically about providing an interface that throws unresolved promises, but is there any more detailed documentation?


We'll likely add more documentation about this -- however, indeed, the contract fits in those 10 lines. There's not much more that's needed on the integration side.

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).


> depends on how your data solution manages a cache

> 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)?


Every example on those pages has a reference toy implementation in `fakeApi.js`. You can use it as a base -- that's the whole contract that React cares about. We'll describe it in more detail later.


Thanks a lot Dan


This is one of the real bummers about the growth of the "ToDoList" app as the "Hello World" of these frameworks. It's really important to demonstrate backend communication.


Rails is probably a better fit for your needs. If you're primarily concerned with sending forms to and showing data from a server, React isn't going to give you much.

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.

[1] https://github.com/turbolinks/turbolinks


If you're not living in a first world country (or living in Germany lol), you usually don't have a stable internet connection. I've developed a lot of CRUD apps that are core to many businesses on field and without ServiceWorkers (or the manifest based appcache before) and client side databases that are better suited to use from SPAs, I'd rarely have success in any of those projects.

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?


No, it does not. Turbolinks keeps a local cache of previously visited pages so transitions to those seem instantaneous, but this is session-only and can't be populated in advance.


From your second link:

> 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


As the page mentions, we actually do move to the Skeleton state (which you’re describing) as soon as it’s possible.

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.


> Skeleton state

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.


>There is a name for this? Didn't know.

Yes, and it's described here: https://reactjs.org/docs/concurrent-mode-patterns.html#the-t...


Hi Dan! Thanks for the great work. I wanted to discuss a few use-cases where React's new runtime model makes things less predictable:

* 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?


>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.

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.


Hi, the docs are great - and cover a lot of ground.

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.


>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.

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.


Hi Dan, this is great and I really like this. But, I think what made me appreciate this is mostly your algebraic effects blog post. My issue with the examples in the reactjs doc pages is that it compares a naive and (intentionally)buggy implementation to a working implementation, so to evaluate the benefits I have to mentally fix the buggy version with existing react and compare that to the working version. It would be easier to see the difference if it could have a working implementation with current react to compare to the implementation with concurrent react.

Also, I'd love to read something about why throwing promises was chosen over other possibilities.


There is more than one way to fix the buggy versions, so if we compared with a particular non-buggy version, people would complain we didn’t compare with their preferred fix. But that’s fair feedback.

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.


I'll be reading these on the subway home. Thanks for all that you've done!!


great writeup! Very easy and to the point. But if I have to suggest something (and I'm just nitpicking here), an example/section summarising everything at the end would've been the "icing on the cake". May be because I read the entire three pages in one go, I got a bit lost in the "Concurrent UI Patterns" examples, primarily when to use `useTransitions`, `useDeferredValue`, `Suspense` and various anti-patterns/edge cases that the examples mentioned :)


@Dan, fyi, useTransition is already used as a hook name by the quite popular react-spring animation lib. I mean, I can migrate old code bases to import { useTransition as useSpringTransition } or I just import yours as { useTransition as useReactTransition }.

Not a big thing but also not really elegant. How would you deal with this?


Yeah that’s kind of unfortunate. Unintentional. I think one of us will have to rename.


You only have to rename if people import each hook separately.

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...


Thanks a lot for taking the time to write this up, it really makes a difference.


Thanks, Dan. You’re a hero!


I'm a developer who primarily uses ReactJS for the front-end. I chose the library years ago because it just made sense to me. I really liked the syntax (JSX), the lifecycle methods and the quick hot reloads. The only thing at the time that I really didn't like was the build system (browserify, gulp, webpack, babel, etc) as I found it overly complicated. Now all of that is basically invisible with "create-react-app" and I can just get right to building applications in seconds - life is pretty good.

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.


> 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.

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).

[0]: https://gist.github.com/Restuta/cda69e50a853aa64912d


> 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.


> I worry that ReactJS's scope/concerns is growing far beyond the view layer that is was initially designed to be.

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?


Well, with any large, complex system, you have to learn enough about everything to know what not to use. I have been developing applications of many types for decades and it seems every time I look at react (I am responsible for one production react app that I did not write) it seems there is some new major change on how to approach the entire framework...it's daunting. And with all the constant changes to the main underlying technologies (HTML, CSS and JavaScript), it's even more so...


If the Jquery authors said ‘if u don’t like sizzle selector, feel free to use standard dom functions to grab your elements’

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?


> Can I possibly even start the discussion at why the hell these things are being named in this way?

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.


Those are advanced resources. For what they do, they are pretty straightforward, but if you are just starting, you don't need to care about reducers, hooks, suspense. You don't even need to do a SPA to take advantage of react, thus, you don't even need a router to start.


You sound like such a shill. You don’t need anything in this life dude. But let’s say you do, or let’s say a subset of people do, and the feedback they comeback with is ‘this thing could be a lot better’, and shills like you come back with ‘well you probably didn’t need this’. Give it a rest.


Well. You're free to improve on it them. Maybe doing something useful would help you to control your anger issues, dude.


> 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.

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).


In this situation yes, the concurrent mode does feel like a view-layer thing. I just don't want the future of ReactJS to look something like Angular 1 - which may never happen. I just worry that someday the team may decide maybe they should start moving in that direction.


The important thing to remember here is that you don't have to use all of these newfangled features. As stated in the write up, these are primitives for library developers (e.g. apollo-graphql) to build on top of. The only real development that affects end-users in the past year or so is the migration to function components but even then, you can keep using class components (although you do have to rename some method names but even then, the React team provides automatic codemods for that).


> The only real development that affects end-users in the past year or so is the migration to function components

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... :/


"You don't have to use it" is a very common statement when a part of the userbase battles feature creep, complexity or just bad decisions. And it is always a lie. Most projects are driven by collectives or passed to new maintainers, which naturally takes this choice away from you. Optional is never optional.


Vue 2 has honestly been my favorite developer experience of all time, but they're basically turning into a slightly different flavor of React with Vue 3.0 in my opinion. The current API will continue to be officially supported, although I don't expect the same to be true for community docs/tutorials/libraries. So I'm in the process of converting my Vue apps to React.


That is interesting. I know Vue 2 had a huge community of very pleased users but I have not been following the developments of Vue 3. This is basically the general fear I have with React. While there really hasn't been any conversations that I am aware of talking about going beyond the view-layer, I don't want it to be the next Angular 1 which made me want to pull my hair out constantly.


As with all packages. They started slim and nimble, but the scope grows, it gets heavy and bloated.

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 React is a lot more slim than it used to be.


Agreed. I loved React and now only like it. The app I work on at work is an absolute monstrosity with render props and context. You can say oh don’t use if you don’t like it! But in the real world you are forced to use others code.


> Perhaps the React team is getting bored?

No, you're just not solving problems at the scale Facebook is. You probably don't need to use Suspense.


That is true, the applications I build are typically internal business use only and have relatively low number of concurrent users. I just don't want React to suffer from any scope-creep and become the next "do everything" Angular 1 was - which was a nightmare I had to suffer through for a while. While this likely wont ever happen, it's always something I worry about. When you love something you use daily, change is hard (but improvements are always welcome - which this feature could be).


Throwing promises to emulate algebraic effects seems really hacky.

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...)


> push a ECMAScript proposal for proper language support instead

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.


I agree. JavaScript obviously has problems but it’s also the most used development platform of all time.

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.


Markup (aka JSX) in js was already a standard way back in 2004 as E4X. It was however only implemented by firefox and adobe (actionscript).


Members of the React team are active in TC39. There are some features of React that have been replaced by language features (like spreading props into another component).


Any framework that allows me to write simple code which then gets _automatically_ highly optimized to run efficiently and with high/low priority based on the user-interaction and effectively removes race-conditions and makes it more intuitive to deal with loading states is a good thing for me.

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.


This is a good development. Honestly one thing I very much think is a downside about React is the fact that your template is also JavaScript. Don't get me wrong this is incredibly powerful and can be very expressive, but it comes at a cost.

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/


> a downside about React is the fact that your template is also JavaScript

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.


What is it you don't get? The parent post clearly states why this can have performance costs. Other modern frameworks like e.g. Svelte solves rendering without making templates JS.


> performance costs

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.


Sticking to the past? You talk as if JSX is the final solution in dev productivity, after which nothing else is even worthy of consideration. JSX has pros and cons, as does e.g. Vue's and Svelte's templating approaches. Something even better will surely appear, or already has.


All good points. Haven't read the code so I don't know how deep the similarity goes but the new React Concurrent UI Patterns is at a surface level conceptually similar to Ember's interruptible route transitions, though more fine-grained.

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.


What I really like about JSX is when you use it with TypeScript, you get type checking in your view layer all the way to the "top". When switching back to things like MVC Razor or Angular that have a separate template, I often hit bugs that a type system would have prevented.


It's true that React can't pause in the middle of any individual component's render function, but when there are many components being rendered, React is able to pause and interrupt rendering between different component instances' render functions.


This is a wonderful example of a well engineered solution to the wrong problem. React is a wonderful view library, and if limited to that use it is remarkably fast and easy to use.

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.


> React is a wonderful view library

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).


> 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.

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.


You can make it happen by rendering all components first as 'null' or placeholder <div> and pushing them on a queue. Then loop the queue with requestAnimationFrame() and set a batch of components visible for every frame. You can give each component a cost so that each batch will fit in a frame. It's basically a simple component that you can wrap around components that you wish to render in the queue. I have been using a solution like this for a long time in production and it works great.

You can combine this with virtualization if you have lots of non-visible components.


We've done a bit of that, but at that point you're just rolling your own, highly-stateful version of Concurrent Mode.


Since you mention scrolling, it sounds like you are rendering a lot of off-screen content... Have you looked into solutions like react-virtualized? It can definitely speed up the experience


It looks like React concurrent mode also deprioritizes the rendering of off-screen content for you. (I've been browsing the react reconciler source this morning...)


I haven't; the amount of magic going on makes me a little wary, but it does seem like it could be a good solution. Maybe I'll give it a try.


Lazy rendering is way less magic than this concurrent work, plus it's something that is usefull everywhere.


React concurrent mode does this out of the box from what I can see.


> React gives you no options for putting that in a worker or breaking it up.

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?

[1] https://en.wikipedia.org/wiki/React_Fiber


So, I'm not sure exactly how Fiber works, but given the context and what I've read/watched, here's my guess:

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" was the rewrite of React's internal render logic (primarily how it processes the component tree), and was released in 16.0. It's named after the data structure that is used to track individual pieces of work (roughly on a per-component basis).

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.


React Fiber is the internal name of some of React's systems that were necessary in order to support the Concurrent Mode feature.


Fiber is the underlying engine used by concurrent mode.

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.


Out of curiosity, how is it not still "rendering"? Do you prefer "start" because it renders a tree of children?

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.


>Apply separation of concerns properly

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.


> Apply separation of concerns properly

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.


Unless you want a mess, usually you still separate by both. Just because you "cut" a square vertically or horizontally, doesn't mean each strip doesn't have, say, more separating lines that you didn't cut. These concerns should still be separated, even if they aren't separated at the highest levels of your application.

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.


> 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.

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 don't think there's any slow reinvention going on in the way you're describing -- the adoption of ideas from (for example) game development has been a total non-secret since the dawn of the React age. There's definitely slow reimplementation going on though, and unfortunately it's having to be done at the scripting level, rather than the engine level.


Yea you're right, I guess there is a distinction between reinventing and reimplementing. The authors even mentioned that concurrent mode is inspired by "double buffering" from graphics.

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".


That's largely analogous to what people attempt to do with virtualisation, but the ability of things to have unpredictable resizing and reflow behaviours throws a few spanners into the works. My understanding that (at least until recently, things might be different now) is that most game engine optimisations depend on being able to make a lot of assumptions that usually hold true, or being able to calculate most of your scene graph optimisations during a build step.


> They still call it "rendering", even though it now usually includes data fetching.

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-...


The browser gives you 16ms to execute JS if you want to avoid stutter. Even with the tightest, hand rolled, vanilla JavaScript there are plenty of situations where that isn't enough time, especially on low end devices.

Seperation of concerns is all good and well, but there are some product experiences you simply can't do with plain HTML.


And yet we have games that routinely do rendering, networking, state updates, and a bunch of other things in that same time frame. Even in JS, using HTML. Let's not automatically assume the runtime is at fault.


There's nothing inherently special about rendering. If the function is an unresolved promise then it should automatically await, and treat that component as blank or nonexistent. It's just legacy architecture to make render functions sync and blocking.


Without data fetching or manipulating, you have nothing to render.


Do it at the top-level. React components shouldn't be managing their own data fetches or computation. They should take event handlers that indicate something has happened on the page, and then delegate that back up the tree until the App decides what to do with them. The App is then responsible for making the relevant fetches, massaging the data into a form suitable for display on the page, and updating the state. Components, to the greatest extent possible, should be stateless and react only to changes in props. If they do need state it should be for things local to the view (eg. the position of a slider, the currently selected tab, or the hidden/shown state of an expando).

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.


>Do it at the top-level. Which top level?

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.


> Requires a lot of coordination code, duplication, and coupling of your entire component hierarchy to each other no?

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.


Definitely, if you want to say that another framework itself better solves all of the very real problems that need solving... makes perfect sense.

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..


Not being able to move your components without editing your component hierarchy is a code smell. Data should be bound at the level it is consumed. "Prop drilling" is an anti-pattern. Redux is the most common way to solve this problem but you can use anything built on top of the context API.


Not to mention you can still keep your pure UI components separate from your components that have data fetching logic (aka containers).

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.


Every one has reasons for their concerns, and complexity is something to be concerned of... but it seems most critics are arguing from a view which implies they don't support the "declarative component" model itself.

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.)


> Requires a lot of coordination code, duplication, and coupling of your entire component hierarchy to each other no?

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.


Perhaps I don’t fully understand your use case, but would something like MobX solve the problem you describe? Keep your data in stores, components automatically react to changes in them. You could do the same with Redux but MobX gives you finer-gained rerendering (only components who’s data has actually changed rerender)


Redux already has ways to do fine-grained rerendering. (That's why redux has you give selector callbacks that operate on the state instead of just exposing the whole redux state to components. Redux only re-renders the whole component when a selector callback returns a new value.)


Redux still has to evaluate the selector each time since it has no clue if any of it's depedencies have changed.


That's why libraries like reselect use memoized selectors.


Isn't this exactly what hooks allows you to do?


Yes, but don't do it from within your view. Fetch the data you need and provide it to the view.

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.


My general lean here has been to use a library like redux-first-router (https://github.com/faceyspacey/redux-first-router) to have this data lookup stuff happen based on routing and existing Redux state, and conditional rendering wrapping for specific components using that data (for example, https://github.com/acdlite/recompose/blob/master/docs/API.md...).


> Fetch the data you need and provide it to the view.

That's exactly what this feature is doing -- making the view layer more flexible in how it responds to the data received.


As an ignorant and neutral bystander, I’m really curious why this is downvoted and would be interested in reading the rebuttal (seems like I could learn something).


Because 4 or 5 years of real world React development have proven them inadequate suggestions.

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.


No, React was created in opposition to the fact that we used to manually update the DOM because that's all we had tools for.


Rethinking best practices absolutely addressed misapplication of separation of concerns.

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.


No, React was created to allow contractors to write code fast and not worry about optimization.


React isnt the one mandating how its used. It can be used in the way that the GP is talking about, but you can also keep the data entirely separate. Redux et al do exactly that. The main thing that the GP is missing is that, like with video games, in order to keep the view rendering smoothly, you need something like double buffering so that if the render isnt complete, you dont block. That is independent of how the data is being provided.


This seems quite complicated. Rarely the big perf hit is in js. The big perf hit is usually layout, composite and paint cycles. They are eons slower than manipulating a virtual dom tree in js.

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


> Rarely the big perf hit is in js. The big perf hit is usually layout, composite and paint cycles. You can’t interrupt the browsers native paint cycle.

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.


"Nobody is forcing" is never a good answer. If gp is a react developer, his feedback and feeling about the framework matters.


>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 ran into that myself while working on a form heavy project. You can use various react form libraries to help make things easier, but then you need to figure out how to wrap your favorite visual component library. Passing all the properties between the two is really really confusing.

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...

Code: https://github.com/lookfirst/mui-rff


> 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.

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?


>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.


We switched to vueJS and never regretted :)


React.js is literally the only reason I'm still in software engineering. I know I should learn Scala and be a full stack developer again, but at almost 34, I really feel like I've found my niche in frontend React development. I trusted the React team when they released hooks, and I've never loved coding more than now. Everything just makes sense, and it seems to always work when you follow the best practices.

I'm excited to start learning more about Concurrent Mode and Suspense.


As I understand it, the principal behind the VDOM is that renders are cheap and the cost is is negligible compared to real DOM updates. If this is the case, why do you need to keep concurrent VDOMs in memory at once? Why can't you just render the VDOM you need when you need it? I think this is where I'm getting lost on this new feature.


The thing that’s slow is arbitrary JS code people write in components. VDOM is not the point there. We want to make arbitrary JS code people write non-blocking, by using components as the “units of work” where we can yield.

Really though, you’re over focusing on the CPU part. The IO part is much more interesting.


When you say "yield", you're talking about render functions throwing a Promise, right? Is there another kind of yield that arbitrary JS code people write in components can do that I'm missing?


Render functions, to my knowledge, can’t return a promise.

(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.


The new suspense API works by throwing Promises when the data isn't ready yet.


I’m not sure what you mean by “throwing promises”...

You mean returning unresolved promises? Or returning rejected promises? Or just throwing an error?


Literally, _throwing_ a Promise.

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.


No. I mean that we yield between calling different components. We can’t pause a giant blocking call in a render but we can amortise “death by a thousand cuts” across many components.


The problem is that you want to be able to abandon work (e.g. due to new data coming in that invalidated the previous data). So, what you need is to go back to the last complete render and start applying your changes from there. If there were partial changes applied from before, you cant just skip the middle update.


OK I've spent all morning reading about this. I think that when you say "work", you're talking about the work of doing DOM updates. Rendering a VDOM is still considered cheap.


The "work" is the arbitrary JavaScript in the `render()` function. In Concurrent mode, a `render()` function that renders 1000 children can now be interrupted "between" renders of all those children.

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.


The version control analogy in the description was spot on, but a good demo is worth a thousand words, so I will be waiting for a good demo to make conclusions.


It might be asking a lot but to form the right impression, I would really suggest that you get through these docs:

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.


I'm still working my way through the suspense doc (very nice so far...) so please bear with this "in flight" thought.

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.


The meat of this thing is in the 'Patterns' doc. That's the kind of stuff Redux isn't equipped to do.


This CodeSandbox seems relatively instructive: https://codesandbox.io/s/frosty-hermann-bztrp


This demo is helpful. One thing that stands out is that fetchProfileData is made in the global scope. I presume (I hope) that it will be possible to do this from include an useEffect hook instead.


The clearest demo that communicates what this solves was given when it was announced last year.

That clock animation is pure genius:

https://youtu.be/nLF0n9SACd4?t=473


I think CPU branch prediction would be a better analogy.

But version control is a lot more approachable.


"For IO-bound updates (such as fetching code or data from the network), concurrency means that React can start rendering in memory even before all the data arrives, and skip showing jarring empty loading states."

What is there to render but loading states when there's no data?


Initially, maybe not much. Maybe some chrome of the page with navigation or buttons. Then as the data streams in, we can progressively render more of it until we’re ready to show something.

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.


Buttons, headings, labels... Will all be ready when your data arrives.


You can already do that without concurrent mode, though. Let's say you have a navbar (your website's header), a list of things asynchronously retrieved from an API, and a footer.

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.


>when there's no data

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.


Perhaps he meant "before all of the data has arrived", i.e. some of it is already available.


Stale cached data.


On a purely technical level, this is really cool. But I can't help but wonder: if this level of complexity is the answer, maybe we're asking the wrong question.


I am huge React fan, but when I see how complicated are some of the modern React codebases in some "leading" companies I have a strong feeling that we will soon need to go back to drawing board and hit some kind of reset button. As I said, I love React ecosystem, but there is something fundamentally wrong with most front-end developers today. They build bigger and bigger SPAs because they are confident that React ecosystem makes it maintainable, well good luck maintaing React/Redux/Graphql/hooks/bazillion-libs after 5 years. Abstraction is good, but it has its limits.


Sometimes frontend developers overengineer their solutions, yes.

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 just don't buy this idea that there is some kind of separation between React library and React as a platform. I'm building SPAs since 2008, been to too many front-end meetups, have friends in so many different companies its hard to count. NOBODY is using React as a library. Banks, shops, news sites, you name it, everyone is using React+Redux+Typescript+bazilion-libs. Graphql is not a standard yet but it is already very popular, basically every new hired Dev adds new libs or switches to something else. It's a mess.


The thing is, I don't disagree that a large (if not majority) number of developers reach for dependencies by default, instead of carefully considering patterns first.

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.


These new frameworks and things like "create-react-app" have made it easier and easier for junior developers to make immensely complicated apps with immensely complicated tool chains. I think that has a lot to do with how things have gotten to the point described in your comment and many of the others. As you start reading tutorials about Vue or React they tend to quickly stray into other dependencies, and webpack or whatever. The web has gotten more complex. The only way to navigate it wisely is to become wise through experience. There are no shortcuts but we've definitely made it easier for new developers to stumble into a mess.


Most of the time, the complexity comes from the app and its requirements rather than having anything to do with React. Could another framework do a better job of handling and helping you manage that complexity? Maybe, depending on the specific use cases. But my guess is the majority of those apps would be complex regardless of framework.


Clients are never as simple as people seem to think. Look at how complex the iOS/Android stacks are, and they aren't even crossplatform like web is.


Correct me if I'm wrong but I think the reason for all the ceremony you're seeing is that React is trying to implement algebraic effects in JavaScript, which doesn't support them and neither do most other languages.


In an ideal world, the HTML rendering infrastructure would have been built atop a lower level representation that was also accessible to JavaScript and allowed for the construction of one's own rendering loop and pixel by pixel manipulation. This doesn't exist, but you can kind of imagine what it would look like if you imagine a world were the HTML rendering architecture was just a library that ran atop webGL.

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."


Are there any similar UI libraries that are actually less complex? I’m pretty sure the DOM engines in browsers, which people might think of as “simple,” are incomprehensibly complex. I’m sure that the Android and iOS UI libraries are significantly more complex than React.


I don't think Android and iOS UIs are necessarily more complex than React. And yes, I've shipped code in all three, for... way too many years, at this point.

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.


Yes, it's ridiculously easy on Android Studio. You can drag and drop interfaces lol. What would take 10 seconds in Android Studio takes 5 hours of CSS and 2 weeks of testing/debugging on the web. What is impossible or requires esoteric hacks on the web (concurrency) is done on mobile the exact same way it has been done for decades in software engineering (multithreading, CSP, ...) with entire books and college classes about how to do it.


Yes, anyone building apps on websites is doing the wrong thing.

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:

https://cash.app/

I guarantee you that the next Facebook, whatever it is, will not have a web client.


Pretty interesting. Hope someone also writes a summary of how it works. Are they computing the tree in a worker, or have they just modified the rendering api itself to somehow be interrupt-able?


Rendering becomes interruptible. In Concurrent Mode, React will split the work of a single render pass across multiple chunks as time exists within a single frame, yielding back to the browser as needed to let it process events. Once the whole render pass is complete, React will finally "commit" the changes to the DOM.

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 :

https://github.com/sw-yx/concurrent-react-notes


Seems someone has written some of it up here: https://github.com/acdlite/react-fiber-architecture


acdlite has written most of the implementation work that lead to this feature


(2016) - this stuff has been a long time coming


Huh. Throwing promises to manage concurrency is kind of neat and scary (because it's new and different). I remember there was huge performance hit from functions containing `throw` statements in V8, has that been addressed (in V8 or in this project)?


If you’re blocked on network, throw is not gonna put a dent in that. We only use throwing when work is blocked on IO.


Yes, in V8's Turbofan


Rich Harris' excellent talk Rethinking Reactivity has an interesting section where he compares a React Concurrent Mode demo to Svelte: https://youtu.be/AdNJ3fydeao?t=1128


This comparison focuses on just one aspect of Concurrent Mode and misses the big picture. I'll believe it when I see these demos ported to Svelte:

https://reactjs.org/docs/concurrent-mode-patterns.html


So you're saying Svelte might have the best performance, but Concurrent Mode makes it much easier to achieve medium performance (there certainly would be value in that)? Sorry, haven't had time to read all the material yet.


I'm saying that I don't see how you're going to implement these "pending loading state" patterns in Svelte but I'd sure like to be proven wrong.

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 :-)


You deserve a vacation!


I'm relatively new to ReactJS and professional web development, and I've been using (and learning) react-redux. The layer schema makes sense to me; React handles user input, redux manages state, and react-redux ties them together.

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)?


As someone who mostly uses ember.js in the day-to-day, I'm wondering how this compares to the amazingly great ember-concurrency library that uses generators?


Here's their official position on generators: https://github.com/facebook/react/issues/7942#issuecomment-2...


very insightful, thank you


This is solving somewhat different problems. I strongly recommend to go through the examples here: https://reactjs.org/docs/concurrent-mode-patterns.html. I think it’ll make more sense then.


thanks!


It's certainly a problem I sort of dance around in React planning how and when something might render based on what is going on, the amount of rendering and such, and the dance is somewhat never ending.


One of the main weaknesses of vdom is that React now has to solve these issues that browsers already do natively.


Can you elaborate? At a minimum vdom is just doing work you would have had to do manually with DOM apis.


With regular dom/javscript, you can be typing into an input field and updating unrelated DOM and the typing should not be impacted much (unless you are changing a lot of DOM and spiking the cpu).

But with React it has to reconciliate the input field.


"controlled inputs" are certainly the recommended React idiomatic pattern, but you can use standard uncontrolled inputs if you want.


I kinda wonder how the regular dom would solve some of this.

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.


This was the biggest pain point I had when developing a vanilla react app circa two years ago. It's great to see that they've come up with a neat solution that works out of the box!


As an experienced front-end developer I must say that I don't really understand why the complexity of building websites is growing instead of going down. 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. We implement complex "routers" and "server-side-rendering" to show specific content when the URL in the address matches a specific value, while the entire web architecture was made exactly for this purpose, to show some content when you go to an URL. 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.

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.


Part of it is that the web platform itself can't evolve as fast as industry would like it to, so in the meantime we pull together/test out new features we'd like to see in JavaScript, until the web can implement them natively. Just look at jQuery: it was a proving ground for fetch(), document.querySelector(), and more. Now it's unnecessary.

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.


> I do wonder why there isn't so much as a proposal for native HTML templating

There were XHTML with XLSTs. It was widely rejected, most people didn't even see the point.


We need a little more than that, though. You can accomplish that right now with JS template string literals and innerHTML:

  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.


You may be looking for morphdom, which is a very lightweight version of what you want, but of course browser support for this would be nice. https://github.com/patrick-steele-idem/morphdom


Lithtml implements declarative DOM using string literals like this very efficiently.

I'm of the opinion that sticking with React when Lithtml exists is just fear of writing vanilla classes.


That might be because XSLT was very hard to use. There were some XSLT processors, but none of them were in-browser or anything close to that. There were promises of browsers that would implement it, but AFAIK no browser really did. XSLT didn't have a simple syntax either. Looking at XSLT, it basically does what most templating engines do these days. But in a more complicated manner. Just look at Wikipedia's example [1].

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.

[1]: https://en.wikipedia.org/wiki/XSLT#Examples


I am sorry but you are wrong about several technical issues regarding XSLT.

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


Yes, I think the problem is in way the current web ecosystem is evolving. People notice shortcomings of current browsers, they create frameworks that add the needed functionality (usually in a really hacky way that adds a lot of complexity and creates new problems), browsers acknowledge the potential feature (... wait 5-10 years...) feature is implemented, said framework becomes redundant and everyone now has to learn a shiny new framework.

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.


> The real problem is that this is a never-ending cycle.

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.


I do feel that the application use-case has already been reached, yet I do not think this cycle speed is going to change. I do hope that at some point this will stop as this cycle is wasting way too many human brain resources which could be otherwise used on more interesting problems.


> I do feel that the application use-case has already been reached

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 agree that native platforms have failed to give us the things that the web platform gives us, and that we want those things in our solutions, but it does not mean that we will ever actually be able to add in the things that the web lacks - I suppose that we will be able to but I must also entertain the possibility that there is some mutually contradictory thing in the two paradigms that we are not seeing.


>As an experienced front-end developer I must say that I don't really understand why the complexity of building websites is growing instead of going down.

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.


We used to build very complex pre-ajax websites (iframes all the way) for the enterprise. So I can hear the author. It is not easier or cheaper or quicker to build and run and maintain exactly the same thing in 2019 (comparing to 2005) which also makes me wondering if the browser programming became the wheel invention exercise. Cmon, we dont have ie6 any more and you can actually debug js with the amazing tooling these days - solving browser quirks was the major PITA and time waster. So where the time goes now? Learning pecularities of a new fashionable framework which will be out of fashion in a few years.


I'm not sure if I can agree with you there. Do you really think it's more expensive and slower to build an app with the exact same functionality today than 10 years ago? 15?

I would instead argue that websites today tend to have far more features and functionality than their counterparts of 15 years ago.


I can't say for all the websites, sorry, just for my space.


> I don't really understand why the complexity of building websites is growing instead of going down.

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.


I do agree that new features were needed for new functionalities, such as games or actual apps, but the irony makes it that we are discussing this on a site used by millions, a site which doesn't use all those fancy frameworks and features. I was mostly referring to the average site created today, be it either by devs or directly by clients using some CMS like WordPress.

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.


> we are discussing this on a site used by millions, a site which doesn't use all those fancy frameworks and features.

I love HN as well, but this is a niche site for niche purpose.


> 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

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.


These types of comments serve no purpose and really irritate me.

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.


>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.

...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.


In 2004 I had a small company with some friends and we were making a desktop app that we called a media management system, that was basically for managing generating multiple media outputs from single sourced data yadda yadda yadda (I could go more into it), anyway we had some interest and some sales but we ended up running out of money and going bankrupt. I remember there were some interested people that would have thrown us money to keep going longer - but they really thought we should have been a web based application.

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.


When you get down to it, the web is fantastic at displaying information not stored on a user's machine. It's also pretty good at taking user information and moving it somewhere else.

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.


> 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.

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.


Whether web apps are technically the right-shaped peg for a given hole is not the only factor in what technology gets used, and usually not even the biggest one. More often than not, web apps make good business sense, so that's what gets built.

Regardless of where the problem came from, we still have to solve it.


I have no idea why you have to resort to a traditional desktop application just because you want a way to show a loading bar... while something loads.


Have to reviewed how multi-platform desktop apps are developed? Is that really easier and more elegant?


so you'd like for us to go back to QT and such instead of taking advantage of the benefits a model like React has already given us for cross-platform UIs? (correct and performant web, desktop, mobile UIs)


You likely don't have the problems that Facebook is trying to resolve with React.

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.


I've seen forms of this argument (essentially "you don't have the same problems Facebook has") more-or-less since I first started using React over 5 years ago, and it's never held that much water with me.

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!


No worries! It sounds like you're saying you haven't needed to tackle this problem, but React has solved tons of other problems for you.


You likely don't have the problems that Facebook is trying to resolve with React.

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.


Agreed. That's why I'm really happy with concurrent mode making this complex problem a little simpler to solve.


And this is fine but why the facebook-scale problems solver becomes a mainstream library then? How many companies have these problems?


This is how things have worked in tech for at least the last 10 years (which is when I first noticed it). People that don't have the same problems as FAANGs cargo culting FAANG tech because it works for FAANG.


Because React's mental model and ecosystem makes a lot of developers happy regardless of not leveraging the more advanced aspects of both.


> Facebook is trying to resolve with React

You mean like being able to have juniors up and running and writing legible and maintainable (if at the cost of being opinionated) code?


As somebody that's helped teach HTML, CSS, JavaScript, and Python to dozens of brand new developers, React seems to help juniors move faster than ever, IMO.


> As an experienced front-end developer I must say that I don't really understand why the complexity of building websites is growing instead of going down.

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.


I challenge anyone who believes "the complexity building websites is growing" to go ahead and build a web app relying on multiple complex state machines with imperative jQuery calls, and then tell me it's less complex than the equivalent in React.


The complexity comes from getting the tooling installed now and packages.

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 can't just include a file things are going to get more complex.

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

[3] https://www.npmjs.com/package/parcel

[4] https://www.npmjs.com/package/poi


I've used vue by including it. I've used react by including it and babel.

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.


You can use a site like https://unpkg.com/ to include files from most npm packages using a script tag.


What is complex about the tooling?

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?


It's all a spectrum, but generally speaking, Web Frameworks are for Apps (Gmail, Facebook Ads Manager, etc.) and HTML/CSS sites are more for presentational pages, etc. The big problems come about when you try to cross those bounds.

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.


We're only really just beginning to use the full power of the browser... JS frameworks were often too fragile to scale up in size. Much like Vue's changes in v3, these frameworks are now solving these growing pains that come with truely large frontends. ...stuff that goes far beyond just the odd select2 widgets and autocomplete to be a core part of the whole app.


I partially blame the availability of large amounts of bandwidth in many portions of the world. We went from having strict data needs, where websites had to be crafted to be quick and clean, to now where just unoptimized and bulky JS litters the landscape.

I miss the old internet some days.


you mean the days when most software had to be manually installed on your Windows? :-)

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.


> you mean the days when most software had to be manually installed on your Windows? :-)

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?


That's how an app works now. Here is a program to do something external to the browser.


I feel like the amount of knowledge required is definitely going up, but once you have that knowledge I'm not sure it is necessarily more complex to make an app nowadays, like would you rather use jQuery, HTML templates, plain CSS files...?


I went back to jQuery for a project because of windows 10 and webpack 2 issues.

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.


For some cases yes, why not?


I like what I see from Suspense. I've had to implement that pattern myself a couple of times before in Angular; it's common enough to justify a standard approach.

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!


Anybody knows whether generator functions were considered and if they were, reasons why they were not used for this?


Yes! I was also curious about this, because I'm sure you could make generators/yield work for asynchronous interruptible rendering, but here's what the React team has to say: https://github.com/facebook/react/issues/7942#issuecomment-2...


> It would be nice if we could “skip” it and wait for some content to load before transitioning to the new screen.

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.


This isn't new. You've always been able to build up a DOM element in memory before adding it to the tree. You can even use something like requestAnimationFrame to incrementally build and not block (useful for things like long lists etc).


For sure. But I’d love to see a demo of features described here that’s as expressive.

The challenge isn’t the low level mechanism. It’s how to make it composable so people can build their own abstractions.

https://reactjs.org/docs/concurrent-mode-patterns.html


Or just implement React natively so speed will not be an issue: https://sciter.com/sciter-4-4-0-0-with-reactor-on-board/


Is this just using generators to allow things to be interrupted and scheduled? Or is it more fancy than that?


Last time I seen an explanation for this, they were talking about throwing and catching promises. So you could probably implement suspense with an error boundary just checking if the error you got is actually promise and if so wait for it to resolve until rerendering.

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)


Here's a comment from Sebastian M (React core team member) about why they chose not to use generator functions: https://github.com/facebook/react/issues/7942#issuecomment-2...


I'm left scratching my head a bit from this blog post. In https://reactjs.org/docs/concurrent-mode-suspense.html, current pitfalls of fetching data are explored including waterfalls (sequential loading spinners for nested components) and data races.

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.


>The way we currently solve these problems, which for some reason didn't make it into the examples, is with Redux:

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.


> As the author of Redux I can assure that it’s not designed for — or capable of — the providing thee features described on this page... Hope that makes sense.

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).


Right, and as a _current_ Redux maintainer, I can also assure you that Redux does nothing to solve those problems :)

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.


I could write a separate comparison of Redux vs Suspense, but it wouldn't make sense to do in React docs because React docs are about... React. Otherwise we'd have to write a comparison for every popular state management solution, which is a lot.

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"


> As the author of Redux

burnnnnnn


These frameworks should be architected to support async/await scenarios natively instead of all these convoluted workarounds. Promises/async have ben around long enough.

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.


The article has a section that addresses exactly this point: https://reactjs.org/docs/concurrent-mode-suspense.html#race-...

Hope this is helpful.


The issue is that the lifecycle methods themselves aren't async right? Async is viral so it would have to be throughout the library, which was my point about it being bolted on instead of the starting point.

Wouldn't making the lifecycle methods async not fix this?


I don't know what "making the lifecycle methods async" means. What are the concrete semantics you are proposing?


I guess I would have to dig in further into the runtime but is there a reason why methods like `componentDidMount` can't be async themselves?

If the entire runtime was async then wouldn't that naturally allow any other user/component level async calls to naturally work as expected?


What would it mean to "make them async"? You're kind of hand waving the gnarly details from which everything else follows.

In some way, Suspense is "making them async" btw. Or, rather, making render async. So maybe we're talking about the same thing.


I'm unsure how to explain so I'll use an example: when C# released async/await, a common complaint was that async functions could only be called if the parent/calling function is also made async, which meant any of its parent functions also had to become async, and so on. It spreads virally until the entire entrypoint for the program had to be async too.

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?


It’s a bit more complex than that... Just making everything async doesn’t solve hard problems like how does your app stays interactive while waiting for something. Ironically that is what Concurrent Mode is all about. So in a way, yes, what you describe is more or less what we did. Seems like we’re talking about the same thing.


React's prioritization of features has led to it being one of the most successful frameworks of all time, I'm not sure how useful it is to suggest they should have started with this from the beginning. It's been overwhelmingly successful without this feature.


It's popular so it's fine? The massive number of async-related workarounds, libraries, tooling and code practices says otherwise. jQuery is even more popular btw.

It's the general state of Javascript and frontend code which is finally starting to understand what multithreading and concurrency is.


JavaScript is not multithreaded.


Javascript as a language isn't, but Javascript code can use multiple threads in various ways.


And you're implying asynchronous coding is new to JavaScript developers?


As default architecture? Yes.


I'd like to see some evidence to back that up. The event loop is one of the few things JavaScript did correctly and it's baked into everything but the most trivial of applications.


I'll refer to my quote: "The massive number of async-related workarounds, libraries, tooling and code practices says otherwise."

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.


jQuery is not an UI framework. It's a library of utilities for DOM querying and manipulation.


The point being popularity has nothing to do with architecture.


The illogical adoption of React must not be questioned, even in the face of its poor performance and ugly syntax. One must never note that perhaps it is simply a cultish adoration, or faddish adherence to popular mass opinions, that leads to "popularity" mattering more than practicality.

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.


Is there anything more in-bad-faith than assuming everyone who uses X is just an idiot, trend-follower, or beginner? Unlike you, the Enlightened One?

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.


> can't use regular HTML, can't use regular CSS, can't use async/await

I'm pretty sure I'm being baited here but all of these are categorically false.


I've been building websites in some form for at least 23 years, and professionally on-and-off for over 18.

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.



Both promises and async/await (basivally just a syntactic sugar for promises) are pretty unpleasant to use in a more complex app.

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.


Aren't reducer-based stores (redux, useReducer) effectively "FSMs with pure functions"? These have been quite popular since very early in React's history. Am I missing something?


Yes, but those don't help manage async fetch jobs.


State machines is what async/await and promises are. They're higher-level constructs built on the concept of iterators (with yield/generator being the lower-level), and the async syntax was designed for easy code flow without having to think about callbacks.

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.


That may be an implementation detail, but to a programmer, promises and thus async/await are just a resource that represents some background operation with some side effect attached to some undetermined time in the future when the promise settles, if ever.

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.


I think the problem is there is no good cancelation functionality in promises/await. Generator functions could work, not sure if the issue was support or that generator syntax is not that familiar to JS devs.


I just want to take a moment and say , while Dan is smart and has probably the right patterns we all need to implement, he is a horrific API writer.

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.


Dan didn't write the entire API himself. I'm sure the whole React core team contributed in some fashion and it went through various code reviews. No need to bash.


Well you know not to be too snarky, but some of these framework writers now days would argue ‘bashing has its place, not everyone needs to be bashing’

I’m just showing you that a shitty version of discourse is available if you need it for your use case.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: