The author of this series has done an excellent job breaking down some of key aspects of React's rendering into good explanations with some nifty illustrations.
As linked in this article, folks might also be interested in my very extensive post "A (Mostly) Complete Guide to React Rendering Behavior" [0], which goes into a lot more detail on the nuances of this.
I also generally recommend reading some of Dan Abramov's posts that explain how and why React works, including the "Complete Guide to `useEffect`" [1], "React as a UI Runtime" [2], and "Before You memo()" [3]. Finally, it's worth noting that some of React's batching behavior will change in React 18 to always batch renders even across event loop ticks - see the React 18 Working Group post on "Automatic Batching for Fewer Renders" [4] for details.
FWIW, some of this material is being incorporated into the upcoming React docs rewrite [5], which I'm really excited to see go live.
You seem very knowlegable witb React abd really I cant wrap my head around it. Di you know any a short tutorial that would show best practices and shows the strengths of React?
Great article but at the same time the absolute horror of what "modern" frontend development is.
I do understand the need for React when building truly complex applications, like a spreadsheet or word processor app, but I wouldn't be surprised if the majority of projects using React could be solved with a simple template rendered in the backend.
From personal experience I can tell you that using a modern framework can be really worth the added complexity even in simple applications.
For one of my clients projects it started out in what seemed like a simple crud app and so I did the few reactive changes in pure java script. The thing is: requirements changed and now after a while I couldn't get around adding VueJS simply because the reactive changes kept piling up.
Even though the HN sentiment seems to be that all "modern" frameworks are just huge piles of code that you add to your project without understanding how any of this works, my personal opinion is that using something like nextjs with server side rendering is not all that different from using a server side php framework like laravel. You still generate the same html unless you need reactive changes and when you do, you are happy to have chosen that. And to play a bit the devil's advocate: Unless you write your own webserver and template rendering engine you are already using a bunch of code you don't really understand.
It is like questioning about backend using a spring boot in every single service even when it is only for a simple store and fetch json from mongoDB. If you have the know-how, why not? Jumping between Vanilla and React projects can be counterproductive. If things get more complex you are prepared, if not, nobody will mind.
It is not fair to compare SPA or big frontend frameworks with "pure JavaScript" or messy jQuery as some imply.
By putting 20% the effort we put nowadays with these big frameworks into well organized "sprinkles" it is a very viable alternatives. Tools such as Stimulus, unpoly, htmx,etc make this very approachable and maintainable.
To me, SPAs only make sense in two scenarios: 1) You have the requirement for the app to work offline, or on really bad network connections (subway, etc) or 2) You already have a team which only wants to do SPAs and everything else is uncool to them.
> I do understand the need for React when building truly complex applications, like a spreadsheet or word processor app
I am not sure about that. Look at Google. For their truly complex applications like a spreadsheet or word processor (Google docs) they seem to have given up on the DOM altogether and are reimplementing the whole UI in HTML5 canvas.
> I wouldn't be surprised if the majority of projects using React could be solved with a simple template rendered in the backend
Yes, Google and the rest of the folks who are pushing for web components believe they can be.
React is indeed a complex library. For simple apps where components are simple and stateless React is easy. When you have more complex apps where components manage their own state and you have to also update the stateful component from outside, that's when the complexity starts. If the data rendered by the component is large and can't be easily cloned then it gets even more complicated.
> solved with a simple template rendered in the backend
Or a simple template rendered in the frontend, see this demo, where no huge libs are used. Instead a 200-line lib is used to get the equivalent of stateless React components:
https://github.com/wisercoder/eureka
THIS. Server side templates and something like Unpoly is what 90% of projects need.
But here I am at work, doing CRUD forms with rxjs, epics, redux, websockets and a shitton of home made packages for doing validation, lazy translations, mixing microfrontends, etc, etc.
Unpoly looks really nice, something I need to investigate as a comparison against htmx.
Server Side Events (SSE) is better option to websockets IMHO, it is a much simpler protocol so you can implement in any backend of choice with a few lines because it just a HTTP request that is kept open for a longer period of time.
Websockets are bidirectional whereas SSE are unidirectional, but you rarely need the client to push huge data sets to the server.
And if you already have endpoints for fetching HTML fragments for rendering a components, SSE fits well as enhancement into that architecture to get a more live experience.
e.g.
model update in backend -> notify frontend with SSE that model changed -> component that renders model fetches new HTML fragment over HTTP.
This makes the messages over SSE tiny, only need to say that a model changed, not the entire model data. This means that you can turn off SSE and the applications still works, just without live updates thru push, e.g switch to timer based solution instead.
What is also nice by fetching HTML fragments over HTTP is that you use cache headers, if you already have the latest fragment that is just a 304 Not modified.
I think that SSR with dynamic updates of HTML fragments closer respects how the web was intended.
I worked on multiple webapps that used react and, in a way yes, you could do server-side rendering. But the real advantage of react (and most other framework for that matter) is to be able to easily program pages that don't need to fully reload everytime you click on something. Sure, you can still do the same with SSR, but framework make it that much more simple and help a lot with separation of concern.
Also, it make task splitting between frontend and backend engineer that much more easier.
It is definitely possible to do structured dynamic updates without page reloads with SSR because that is what we did 15 years ago before the JSON hype. I know because I worked with it then.
The need to separate the team into frontend & backend engineers is a direct consequence of the over complicated frontend frameworks.
That is another tragedy of this heavy frontend trend. We are undermining the team’s performance and makes it harder to finish deadlines.
Usually what happens is that the frontend & backend engineers get out of sync and starts working on other things until they can sync again.
When this happens too often there is push to put the backend and the frontend in different teams.
After the split the teams start evolving separate cultures and mindset how to solve things and soon enough both teams has their own build and deploy pipes.
Eventually the teams no longer feel that they share a mutual idea of common ownership and the project stagnates.
If we keep things nice & simple we can work as fullstack engineers together as a team.
I have to say stuff like this is why I'm not a big fan of the component approach to frontend dev. Reasoning about when and why something gets updated is not very intuitive.
The other thing that tends to happen is that components become god objects. People stuff all their business logic in there, and suddenly most files in the project have a tsx or jsx extension. It's winforms code behind all over again. "React is a library not a framework" starts sounding pretty hollow.
It's possible to not do this, so I can't strictly blame react for this - but it's happened in every react project I've seen.
This is confusing because before component models, everything was a global.
I do agree about one thing though, before hooks, the standard recommendation was redux (or similar) I kept SCREAMING that it was a giant global anti-pattern. I don't think anyone understood that for the first 5 years of React. It seems like that whole thing is corrected now.
> This is confusing because before component models, everything was a global.
Hot take: I'd argue this is confusing because (almost) everything should be a global in UI development. Plenty of websites were built in PHP and basic Javascript with (gasp) globals! I guess some folks don't understand that people have been making pretty complex web apps since before 2013. It's monumentally counterproductive having to deal with state management in like 40 nested components because you just want the username and user email to trickle down.
So people re-invented global state but we call it Redux now. It's a parody of a parody.
Globals are bad in general. Things should be as specific as they can be. If you were playing a tetris game and suddenly wanted to upgrade it and make it a DUALING side-by-side tetris game, then all your state is going to collide. It would have been better to define your <Tetris> tag to have it's own state.
It goes further than that, but essentially it's all about maintaining a context for a thing. I have a patient, that has meds and records etc, I need to keep that contained to one place so that I can have a SECOND patient at the same time (if I am writing an EHR for example). The original "UI of the web" was Open a thing, edit a thing, save and close a thing. And all of that was like 6 full page refreshes.
More apps should be like Trello, for example, where you can just edit a lot of state on lots of different objects super quickly. Globals don't work well for that.
I’m not convinced that this argument holds for the specific case of “a single global source of truth, with a structured and enforced update mechanism.”
Let’s expand your <Tetris> example. You didn’t specify whether both players are on the same computer or not. I agree that my <Tetris> instance should not depend on or conflict with any other player’s <Tetris> instance. What does that look like in practice?
Whether it’s local or internet play, we need a way to isolate each player’s game state. Each <Tetris> instance should only be concerned with displaying a single game state, and responding to at most one player’s inputs. What happens outside of that is not its concern: things like maybe logging into a server, knowing that there is a second <Tetris> instance running alongside it, knowing where garbage blocks came from, maybe knowing that my friends are online, etc.
To me, the obvious choice is to use something like
<Tetris state={app.player[0].gameState} />
If this is local multiplayer, there could be another <Tetris state={app.player[1].gameState} /> which has different input bindings. If it’s internet play, it could look the same and not have anything bound to inputs.
In either case, <Tetris> knows nothing about where its state comes from or goes to. But that state has to live somewhere for the app to access it, so where is it stored?
Redux doesn't really have anything to do with passing data around in an app. Redux is only a state manager and reducer. Accessing that state manager in components is usually done using a React context provider (in a React app... Redux can be used with any library).
Recent versions of Redux include a useSelector hook that manages that context provider for you, which is probably where some of the confusion comes from.
> Recent versions of Redux include a useSelector hook that manages that context provider for you, which is probably where some of the confusion comes from.
There's no confusion, Redux absolutely does deal with passing around data in your app, that's why there's literally an FAQ dedicated to how to do it[1][2] -- and whether or not it should be done via setState or the Redux store. It's a stupid solution to a stupid problem, but now we're stuck here until TikTok will inevitably come up with their own "ground-breaking" React replacement in 5 or so years and we'll start the whole cycle again.
Nothing on that page is about passing data around. It's about whether or not you should manage state locally in the component, or globally in the state manager. In cases where you choose to hold the data in the global state manager you access the state manager (in React) via a context provider. That context provider is not a part of Redux - it's react-redux as isakkeyten noted.
I understand that this probably feels incredibly pedantic but it's quite important to frontend engineers like me. Redux is only a state manager, and it is not a React library.
well I sort of love to be the guy that asks if there is some other library that binds to redux that I will love, and thus also cause me to feel some sleight love by association for redux?
>Redux doesn't really have anything to do with passing data around in an app. Redux is only a state manager and reducer.
If you tell component X what state component Y is in you are in some way passing data around in your app. Since Redux manages the state it is part of the chosen strategy for how data should be passed around in the app.
In the same line of thinking: I always wonder why people look bad at you if you suggest to use an event bus or dispatch events on an element but it is totally fine to dispatch actions in redux. I don't see the difference other than one being cool and the other being "old".
Hooks don't really change anything about whether or not you use Redux, though. The only real difference is that there's now the `useReducer` hook, but that's just a different mechanism for updating the same component state we've always had architecturally.
FWIW, I maintain Redux, and I've written extensively trying to clarify when it does and doesn't make sense to use Redux, and how to use it the right way [0] [1] [2] [3] [4] [5]. No question there's been a lot of FUD and misconceptions about that over the years, and I wish I didn't have to spend so much time on this topic, but lots of people never look at our docs and actual recommendations.
Also worth noting that our Redux Toolkit package [6] and React-Redux hooks API [7] have made Redux _much_ easier to learn and use than it was previously, and we get a ton of very positive feedback about that.
Before react and vue everyone could create a mess, but it was not mandatory. Desktop frameworks always used sorts of controller hierarchy vs view hierarchy separation and didn’t store global data. There was decent ui before web/react (though it may sound crazy to react guys).
I don't think anyone understood that for the first 5 years of React
I bet that millions of developers who were not on the ~awesome hype train~ would look at this statement with a great confusion.
I’m not a react guy though, can someone explain where the data usually resides now? Right in a component state, I guess?
- Try Vue[0], Lit[1] (it's a bit cutting edge but I really like it), or the other entries in the component-building library space
- Don't use redux/vuex all of that, and just write plain javascript objects that make fetch() calls
- Don't use hooks (yet)
If you do these things, you should find the component approach refreshing compared to copying and pasting HTML and managing event handlers/spaghetti and making your own structure. For all but the simplest of pages there should be an improvement over vanilla HTML+JS.
If it makes you feel better, if you squint React looks exactly like Backbone views, which had a reason for existing back when they did, and if you go even further back it's like COM[2].
This is a puzzling advice to give to someone who wants to offload business logic away from components, as redux with sagas for example is a great place to put it. That way if you use selectors (preferably memoized as well) your components will only update when needed and you can organize business logic however you want.
It didn’t seem like the commenter didn’t know what Redux was, but more that they in using it as prescribed they still ran into the same issues. I was suggesting not using/needing redux mostly to reduce the starting level of complexity
I mean gp mentions they don't like the god components, and in my experience the kind of project where this becomes a problem is likely not using redux. Nowadays, redux is pretty well documented and there are ways to reduce boilerplate (for example, redux-toolkit) so I feel like it's a pretty good option to consider.
Yeah I think it's a real shame, most of my zeal these days on the front end is finding the smallest most focused libraries that do certain things -- I always shudder when I see what seems like pattern explosion/new development in libraries like React.
Of course, most of this is not React's fault -- they blazed the trail, and it's not like they could have known to build in the semi-automatic reactive system that Vue did right ouf o the gate. That said, KnockoutJS and the whole MVVM revolution along with RXJS were around to learn from.
I just tried the pattern you recommended using Vue to build a simple Purchase Power Parity Calculator[1] as all current ones are outdated AFAIK.
I'm a least JS, 'You Might Not Need JQuery' kind of person so my complex web applications are all rendered in the back-end using Go but this particular website is static and this is the first time I've added some JS to it for a core function.
It does seem like Vue is a good fit for small front end reactive components like this, I'm refraining from passing further comments on the subject as this is the first time I've tried it.
Love the project, it works well, loads fast, and I'm glad you didn't fall into any crazy frontend pitfalls.
I think vue is great for small apps and "big" apps, and I think others would agree. I mostly disagree on just how much value some of the enterprise-grade patterns that show up have.
I agree with your use cases for Vue, It enables quick component development with minimal learning curve and good performance. A good choice for someone like me who had been holding out on modern JS libraries to offer better usability.
There seems to be more lighter version of Vue[1] being developed[1] which seems to offer more flexibility in terms of how light we would want it to be and looks similar to Lit, Did you recommended Lit for the same reason?
You can't ask for more than that from someone who disagrees and is in control of the project. Also he's doing a pretty awesome job maintaining the project.
The posted link contains a detailed breakdown written by Vue's creator of why Vue, a similar tool to React chose to de-emphasize class-based components in favor of the composition API (hooks).
I thought you might be able to find some clues to why people pick hooks over class-based components in there.
It's because while React offers a decent standard for declaring components, there is no standard for building an application with React as the rendering layer. I mean I've got an `api.ts` file somewhere with a TODO that says "I should probably split up API requests and business logic", but that's all handcrafted guesswork.
Angular, for all its flaws and the major mistake that was announcing Angular 2 to be a rewrite etc causing a big exodus, at least had an Opinion on how to build an application end to end.
Yep, Angular 2+ is so much better than React and Vue precisely because it's so opinionated. In 4 years of using it I've only really had issues with serverside rendering and even that got resolved on the repo pretty quickly
Every project I've inherited has done this, and every time I go through the tedious process of separating UI logic from business logic. If done the right way, a component-oriented approach can be very nice.
But yes, if a dev thinks that React or Vue or whatever is somehow going to automatically create good architecture for them, then they're in for a bad time.
This is why you need to ask everyone who interviews "What is Container vs Presentational, and why is it good/bad". If you cannot give you a good answer don't hire them.
You may want to look at SolidJS, and read some of the articles by its creator. The component model of dev doesn't imply any of this, even if the rendering approaches might learn from each other. Specifically: Solid has a component dev approach, but components vanish at runtime and it's just your reactive calls and the DOM from then on.
The alternative is manually hooking up a bunch of views that update by subscribing to some event bus, which quickly becomes unmanageable. I don't want to learn about all the custom events in the system to know how things update. And god help you if you have cascading/recursive events.
Your mental model for updates can be shared across all apps that use React and it's a huge blessing for getting up and running with an unfamiliar codebase.
That’s more a consequence of hooks & context in React specifically , rather than the component model. There are other frameworks where it’s much easier to reason about updates.
React encourages you to push state down, into components low in the tree, as seen in this article, which leads to "state and templates being in the same thing". But that is not a property of the component model; you can keep most of your components pure if you'd like, and keep the state and logic at a higher level. At that point there is little distinction between components and "sub views" in a template system. It would just be quite painful to work with.
I do agree with your other point - it's almost impossible to use React as 'just a view library', it ends up permeating everything. I believe the hooks API aggravates both of these problems: the former by making it easier than the alternative patterns, the latter by creating function 'colors' that are contagious.
Cool but I would like to understand the opposite: why doesn't React then just memoize everything by default? Why not let the developer opt-out of memoizing instead of having them opt-in?
Memoization isn't a free lunch. I think React's approach is to keep it simple with not managing cache stores for absolutely everything. Most small to medium react apps need little to no use of useMemo to be performant.
If I remember correctly, React's default approach of comparing states/props is a shallow comparison (e.g. for PureComponent). That seems about as close to free as you can get.
I use PureComponent everywhere, but I don't know what people do in function-component world.
When doing functional programming, immutable lists with value comparison are what you want. However with JS, you only get mutable arrays with reference equality out-of-the-box. You can use a library, as others have point out, but then your code gets very noisy.
It's not calling entire tree, when you do any hook/state changes only that component and everything down is called. Also, it's still not doing any dom changes, although I'm not sure what kind of performance impact it has nowadays. As to why, I don't really know, but as a dev using react I sure enjoy that I don't have to worry about some component losing track of state.
As linked in this article, folks might also be interested in my very extensive post "A (Mostly) Complete Guide to React Rendering Behavior" [0], which goes into a lot more detail on the nuances of this.
I also generally recommend reading some of Dan Abramov's posts that explain how and why React works, including the "Complete Guide to `useEffect`" [1], "React as a UI Runtime" [2], and "Before You memo()" [3]. Finally, it's worth noting that some of React's batching behavior will change in React 18 to always batch renders even across event loop ticks - see the React 18 Working Group post on "Automatic Batching for Fewer Renders" [4] for details.
FWIW, some of this material is being incorporated into the upcoming React docs rewrite [5], which I'm really excited to see go live.
[0] https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...
[1] https://overreacted.io/a-complete-guide-to-useeffect/
[2] https://overreacted.io/react-as-a-ui-runtime/
[3] https://overreacted.io/before-you-memo/
[4] https://github.com/reactwg/react-18/discussions/21
[5] https://github.com/reactjs/reactjs.org/issues/3308