This gives more information than any other analytical comment. Someone who has used both to build the same application and can compare the experience between both.
It's not the final say. Just a strong signal among many others.
It doesn't look like a very complex application, but already has a ton of abstractions, essentially building your own, unproven and unreviewed framework.
A function is an abstraction. A JSON object is too. Are we in preschool here, not allowed to create new abstractions because they're "unproven and unreviewed" by a FAANG?
This all seems so narrow and parochial, like people who have never left their own village. There's a wide world of useful technologies and design techniques out there (including React) and the simplest possible design that solves your problems is usually best.
You are allowed to do practically anything. I'm just stating that I don't think it's an improvement to replace battle-hardened well understood abstractions for half-baked new ones you have to learn on every new project.
People running away from "bloated frameworks" very often end up reimplementing a significant portion of the bloat. But usually with fewer ressources and fewer testing, so you can guess what happens most of the time...
Project Manager: Now add 3 checkboxes, one being 'N/A' and when you select N/A the other checkboxes are disabled and unchecked. And update the labels, and have additional form sections showing depending on which checkbox is displaying.
Probably not that difficult, and probably a bad example, but with React/Vue/etc. it's a lot easier to deal with complex states than manually manipulating the DOM.
From experience trying to be clever and anti-framework ends up just creating another framework that's worse in every imaginable way.
Hum... Your new function reads all the checks, calculate the new values and enabled conditions, and applies them. It's called by the onChange of all of the relevant elements.
Reactive frameworks do make your life simpler (at least once you pay for the large one time cost of using them), but the change isn't enormous.
Better question to ask is why so many libraries/frameworks proliferate in webdev and not as much in native apps. JS is a suitably powerful language these days.
Also, the attitude of “just use jQuery like everyone else” (paraphrasing the last paragraph and substituting for React) would have precluded the development of React.
The cleverness is abstracted from you as a developer and the cleverness is maintained by an open-source community and a huge corporation (Facebook).
With native apps you definitely have frameworks available now. React Native for an example. But the languages and toolkits available basically serve as a framework anyway.
Is cleverness only allowed for the FAANGs of the world? I cannot subscribe to such a nihilistic view of computation.
SICP should be required reading for any serious dev if only to expand their imagination beyond an industry that is intensely focused on boiling the difficult and creative endeavor we call software development into a discrete, repeatable process.
I mentioned the open-source community in my comment. So a collective of knowledge of many talented developers from the public, and also the stewardship of a FAANG and their developer talent pool also.
Well react itself is just the virtual diff algo really.
How to change this page to this other page as fast as possible, if you don’t know or don’t care to know that other page.
That second qualifier is really important. As long as you have 2-10 screens and know the transitions by heart, nothing stopping you from hand rolling js. But once you start adding states things get tricky really fast, as transitions grow exponentially.
I’ve built some very complicated apps with vanilla js back in the day, and we had ways of dealing with things like that. We called it “reloading the page” where you start from 0 with your state.
Kinda like restarting windows to fix it, rather than figuring out whats wrong.
And you could get quite far that way. 37signal’s basecamp was like that - an html app with vanilla js sprinkled throughout. Worked great.
But there is a limit in complexity. JS and html are great for building websites, but if you want to build an actual application, you need to be really clever and accept a lot of limitations. React just lifts the ceiling of what you can do, without being all to complicated.
It seems like it's a non-effective way of doing it. In large DOM trees you need to manually interfere to make them update fast because (1) building even the virtual DOM and (2) then comparing this to the real DOM become slow => i.e the "React way" doesn't actually scale. Perhaps for a product guy this doesn't matter, but for an engineer it just feels wrong. Why not solve the real problem?
Because the real DOM exists in a separate world where you have to cross a trust boundary. The vdom is an implementation detail of React because the real DOM is too slow to traverse to diff on every render. Think of it like this.. jquery is a wrapper around a querying API, so it’s like running a sql query that can be indexed in a b-tree or a hash map, because you query by id or class or some other attribute. React on the other hand needs the entire state, which is equivalent to a select *.
If you say why does react need the whole state, it’s because React is a translation layer between an immediate mode representation and a retained mode system (the DOM). To illustrate this in a basic way, think of a checkbox. It is an entity in the DOM, but it is also retaining state itself (there is a Checked property that persists as long as it is set, so you know it has state). The only way to know if that checkbox is checked or not is to query the DOM to get the checkbox entity, or to “control” the rendering of the component so you can always derive the checked state from somewhere in your code, and force its state to match. React controlled components are a way to express unidirectional state from your code to the UI.
What situation were you in where vdom was the problem? I’d love to see an example. It is likely that vdom was not the performance issue, something was wrong with the implementation and causing a full re-render. Could you link a sandbox?
I understand why it's convenient to derive the UI from the state, no need to explain that.
But what react does is: for any change in state it creates the entire virtual DOM" regardless of if there was a single checkbox change and everything else remained the same. Then it compares this whole tree* to the real DOM, and then only replaces the parts that changed. The "win" of react is that instead of rendering the entire real DOM again and again it just renders something that is not seen and then swaps out the changed parts in the real DOM.
Why not short-circuit from changes in state to real DOM instead?
I think any google sheet like app would do: you type into some box, this changes state which triggers the creation of the virtual DOM for the entire sheet. Then it compares this entire sheet to the real DOM, finds that only one cell has changed and swaps that out. There will be one entire virtual DOM per character typed. Which is why I'd presume most if not all such applications need to add custom code (using `shouldComponentUpdate`) to fix this scaling issue.
wasn't this done because e.g. updating manually a thousand elements one by one was shown to be, like, orders of magnitude slower than updating the whole thing ?
On the one hand it is rare that you'd need to update a thousand elements at once one by one.
On the other hand, and more importantly, the problem that Virtual DOM aims to solve is not that one. Instead, what it wants to avoid is not "updating a thousand elements" but "replacing large sub-trees in the DOM unnecessarily".
That is, Virtual DOM competes, mostly, against doing something like...
someElement.innerHTML = '...<a large piece of HTML>...'
The main concern of doing this is not so much that the performance is bad or not, but that it is intrinsically wasteful. That is, it [almost always] unnecessarily forces the browser to rebuild a part of the DOM and re-render a part of the page... just to change but a few values.
This is written in that way because it's easy to write it like that. But, being such a wasteful approach, it does end causing performance concerns.
Going from this, you can use various approaches. A Virtual DOM basically let's you manage your [virtual] DOM fragments as you were already doing but then inserts itself as a middleman to avoid doing those unnecessary DOM replacements and only modify what needs to be modified.
There are two things you need to notice here: First it does add some additional processing an calculation -"the diff"-. Second it still, because there is no other way to do it, uses the same DOM API calls you would use if "manually updating things".
So now you can balance this to see if it's worth it: On the one hand there's a cost -the added calculations-, on the other hand there is a gain -modifying only smaller parts of the DOM-. But be aware that this gain is compared not against "updating many elements" but against "rebuilding parts of the DOM when you don't need to". The distinction is rather important because if, suppose, you actually need to update the content of say a thousand <td>s in a table, your virtual DOM library will still need to do exactly that, it will do so by using the DOM API -because there's no other way-, and any additional processing it does will be added on top of doing that.
...every time and dump it all into the DOM element; let the browser do all the work and live with the bad performance you get from having easy to write but wasteful code.
Virtual DOM solution: Still write easy to write and wasteful code but apply something in between that reads your "whole block" and finds what actually needs to be done and then does only that.
But it's not the only solution. Another solution is to just don't write that wasteful code in the first place. Instead of building large but mostly unchanged blocks and dump them, only modify what needs to be modified. Of course, it usually means that your code has to keep track of an additional bunch of things -i.e. where in the DOM does each piece of information go-, but this is where those other alternatives mentioned, like Svelte or others, may come into play.
The conclusion from all this is that:
a. No, a virtual DOM is not inherently faster than doing updates manually
b. A virtual DOM provides some gain by not doing unneeded updates of DOM branches. If you were doing those, using a virtual DOM may speed things up.
c. A virtual DOM still needs to do the DOM modifications with exactly the same API you would/could use manually so there's no gain there.
> On the one hand it is rare that you'd need to update a thousand elements at once one by one.
Is it ? I come from the desktop app world and that would frankly barely register above "toy project" ; my experience is more around a few tens / hundred of thousands objects that can change at once (with of course the UI framework only redrawing what needs redrawing).
> There are two things you need to notice here: First it does add some additional processing an calculation -"the diff"-. Second it still, because there is no other way to do it, uses the same DOM API calls you would use if "manually updating things".
I have a hard time understanding how, say, calling 500 methods per second on a DOM object, e.g. `label.innerText = someSensorValueComingFromWebsocketsVeryFast;`, which needs to trigger various events, callbacks, etc... every time could possibly be faster than modifying a pure JS object at the "incoming message rate" and then blitting that object at the screen refresh rate or something similar ?
> I have a hard time understanding how, say, calling 500 methods per second on a DOM object, e.g. `label.innerText = someSensorValueComingFromWebsocketsVeryFast;`, which needs to trigger various events, callbacks, etc... every time could possibly be faster than modifying a pure JS object at the "incoming message rate" and then blitting that object at the screen refresh rate or something similar ?
Updating a value on the DOM 500 times per second just because you can read it 500 times per second falls, again, in the category of writing wasteful code just because it's simpler. i.e. "don't care about performance, just let the browser work".
If you compare code which is initially bad, then sure, a lot of "solutions" will be better than that.
The appropriate comparison for evaluating the value of using a virtual DOM should not be about that, but only about the part you describe as "blitting the object to the screen". You have the data, no matter how, where, or even -to some extent- how often, and you want to put it on the screen.
> The appropriate comparison for evaluating the value of using a virtual DOM should not be about that, but only about the part you describe as "blitting the object to the screen". You have the data, no matter how, where, or even -to some extent- how often, and you want to put it on the screen.
But the solution to the problem which is
how to go from
inputs (which you don't have any control over)
to
"You have the data, no matter how, where, or even -to some extent- how often, and you want to put it on the screen"
Maybe you can find some rendering comparisons from a few years ago. There was some "demo" called DBMonster or DBMON that showed the performance of updating a large amount of values on a table in a web page. The thing is a lot of different implementations were then written by many people using many different frameworks -including many using a virtual DOM and many not using one- and also even some "vanilla" implementations. I don't suggest it for the performance comparison -which you can of course look into- but, more in line with the question: it may be a good way to find a number of different approaches.
> That is, it [almost always] unnecessarily forces the browser to rebuild a part of the DOM and re-render a part of the page... just to change but a few values.
That's a mere implementation detail. In practice, letting the browser re-render a single page chunk using its internal implementation will almost always be faster than using JS code to serially update any number of DOM elements. VDOM diffing is a kludge to make DOM manipulation in pure JS usable, it's not actually going to be faster than relying on the browser's own rendering. The biggest problem with the innerHTML is going to be generating the actual HTML, but one can most likely use WASM to speed that up.
I don't think so. I believe it's because without a virtual DOM and a diff, how would react do rendering at all? The whole point of react is you just have a functional "render" method, so you need to render _somewhere_ (and it can't be the DOM for a variety of reasons, eg. deleting and rebuilding the whole DOM would be slow and break things like focus).
Svelte is different and doesn't export a render function -- the component knows what DOM nodes need to change for a given prop/reactive var change, so it doesn't need a virtual DOM (and can be much faster as a result)
"I hate people advocating for a tool by bringing up the problem it was used to solve. Just keep your app so simple that you don't need the tool."
A better solution is to emulate the react strategy. Create a state object, on user input you can rewrite sections DOM based on the state. You probably don't need React's diffing algorithm until you run into performance problems. Then you have to limit which parts of the Dom you update to keep it snappy. Then you might as well just use react.
> "I hate people advocating for a tool by bringing up the problem it was used to solve. Just keep your app so simple that you don't need the tool."
Casually dismissing others by strawmanning them with fake quotes is against the rules. Even outside of that, it's just generally the kind of thing that is neither respectful or respectable. Please don't do it.
The beautiful thing about React is not React itself, but rather how much it pushed functional idioms into mainstream programming.
We can do better from here, mostly by hewing more to the functional core/imperative shell idea. Redux is a popular approach that shows the power of this. In more modern languages with ADTs you don’t even need a library/framework once you fully understand how to structure things well.
And so you just recreate React but worse and make it harder to onboard new developers and can't use any of the great React libraries/components already created by the community.
It's all about scaling of complexity and number of engineers working on the project. No doubt hand rolled js is better when you only have to do the one thing you mentioned and you're the only person who has to read or write the code.
What happens when it's one of hundreds of checkboxes on the site? When they all do slightly different things depending on user sku, product page, file type, file permissions, god knows what else. And when one experiment flag is on or how about this other one? Then i18n? Then what about the fact that after you write this line countless other engineers will see and modify it in the future, it's not just you writing and maintaining it? These are the problems that my company (and, I assume, Publicis Sapient with 20,000 employees according to Google) have to consider when choosing whether to hand roll JS or use a framework.
Everything a programmer does is "hand rolled" whether it's JS or JSX.
You'll still have to tackle those complex requirements and come up with a good design that organizes things and makes them manageable, whether you're using React and JS or just JS.
To say that a good design is just impossible with JS or that no other engineers could possibly understand the resulting code seems absurd.
Javascript is a Turing-complete language. It gives you a lot of design options. Your poor engineers will have to learn it to use React anyway so why would vanilla js necessarily be worse than js plus a giant framework?
I once worked at a place that thought like the grandparent comment. "We're gonna have a huge app with lots of different UI variations. Let's use React. Then it will be super maintainable and scalable!" And then it turned out that if your plan to write maintainable code starts and ends at "use React", you will end up writing code that is not maintainable, and your framework will not save you, and in fact you will be fighting framework-induced bugs and performance issues on top of the problems caused by your total inability to design computer programs. The pace of software development at your company will grind to a halt, you will accept this as the only possible state of affairs (we're following all the best practices by using React!), and your company will limp along for a while and then go out of business.
It's more about the programmers than the underlying technology. But with react (and redux etc) very smart and experienced people have already put some thought into things like maintainability, and your chances of achieving success from the shoulders of these giants are higher than by starting from scratch.
I don't think that's really true. React doesn't give you less problems, it just gives you different problems that aren't as immediately visible because you haven't learned to look for them yet. For example, if you're used to thinking that every page of a web app can be tested individually, you won't notice bugs in single-page apps that only happen when using specific links to arrive on pages in a specific order.
React only gives you tools to reduce your problems.
In the case of single page apps, preventing you from shooting yourself in the foot is outside its scope.
What you are describing sounds more like a general problem in state management, and you'll have that with any framework. A common way to avoid such things in React applications is with Redux.
Unfortunately, even those advanced tools need to be learned and applied.
As used in practice, React has you replace one set of tools with another set of tools. React likes to "own" parts of the DOM tree and crashes or otherwise misbehaves if anything else touches them -- and the way people generally adopt React is by making it "own" larger and larger portions of the page until it "owns" everything on the page. (Or by writing the app so that React "owns" the whole DOM tree from the start.)
And when you use it that way, the way that everyone who has ever used React uses React, it can indeed cause problems of its own! Half the time, the only reason people build single-page apps is because they've destroyed their page-load performance by using heavy JavaScript frameworks! If you make a big nested component hierarchy, and then it turns out you need a component to affect something nine levels below it in the component hierarchy, and you have to modify all the intervening components or rig up a Context provider or something, that is a problem that has been caused by React's design! If you put a piece of state in Redux, and then it turns out you need to access it in a place that does not have easy access to the Redux store object, that is a problem that has been caused by Redux's design!
You can say "ah they just haven't learned to use the tools properly", but then we're back to noticing that anyone capable of using React to write good code doesn't need to use React to write good code.
I've used react for years now and never had those problems. I've used components managed by other libraries and React didn't crash.
There are tons of ways to access state in a redux store elegantly from anywhere in a javascript application. Yes, if you think that is the difficult part, you are clearly doing it wrong...
And please don't write single page applications when you don't know why you need one... There are plenty of reasons why SPAs can provide a better user experience. If you do it right.
Building a doctor's appointment website with a form and a calendar widget? Use vanilla JS or jQuery.
Buidling the next Google Docs or Notion app? Use React/Angular, etc.
99% of the people that use React use it to build the former and then debate endlessly about their little teets and toots of JSX, Redux, Router library, etc. FFS, it is overkill and unnecessary. Simplify, go home on time and enjoy your the time with your family.
> Buidling the next Google Docs or Notion app? Use React/Angular, etc.
Wouldn't the performance of Google Docs suffer too much by using React? If I remember correctly they're switching to canvas-based rendering. Same thing with VSCode, they don't use a framework. I'm not sure about Notion, as I don't use it much, but I've never been impressed by the performance too. I think your best bet for React/Angular is around "medium complexity", so a typical web app that isn't that complex.
This is a content-free take. Swap in any methodology or design technique ("functions", "structured programming", "Unix pipes", "any higher-level programming language than raw assembly"...) and it would be just as meaningful. These are abstractions that help produce a good design to organize things and make them manageable.
React is quite a minimal framework (or not even a framework), actually. It's just that it has a huge following and takes some mind-bending to actually benefit from it.
If your client is an otherwise idle system with the latest browser, a fast wired connection, and the developers carefully monitor time to render.
In most cases, at least one of those is not true and the developers are using something like CRA so your site has a number of users who see tens of seconds of nothing while their Android phone downloads and runs a 1MB JS bundle. Bonus points if the marketing department has tossed in a tag manager which delays that even longer.
> Male gorillas of the Western lowland gorilla (Gorilla gorilla gorilla) subspecies have average weight up to 140 kg (310 pounds) while females have up to 90 kg (200 pounds).
You don't need to get close to that level of complexity for React to become useful.
> Meanwhile I have seen SO MANY sites that use full React for a contact form with two fields on it.
I agree that the contact form you describe would be dead-easy to do vanilla. Maybe throw in Parcel if you want to write ES6.
However, while I suspect some people may use React in this case because it's all they know, others may do it because they are literally faster in React - they have a workflow that can see them code and deploy such a website to Netlify or Heroku or whatever in 10 minutes.
Which is fine if your company and teams use React a lot.
Why introduce inconsistency in everyone's workflow just because this specific page is a simple contact form?
The fact that it is just a simple contact form is exactly why you should use the same tools and workflow that everyone in your team has already been using.
Write what other people knows so you don't have to be the one maintaining it.
Yup. React is a tool so that thousands of people can speak the same language about a big codebase. That's what it's for, that's the only thing it's (arguably) good at. If you want to pretend otherwise you're left to these sorts of mental gymnastics to make the evidence fit your assumptions.
You (and most commenters on this branch) forget the most important thing: there are bazillions of libraries that work with react so I don't have to produce all that untested vanilla code. I only have to wire them together in a way I see fit and then I can jump on the next project. Also: good luck finding a person who would forego using and maintaining their easy-to-market React skills.
None of which is specific to React, as decades of history shows. One reason that history is worth paying attention to is that it confirms that you’ll be spending a lot of time managing dependencies, testing breaking upgrades, and working with upstream maintainers to fix problems and add features. It’s still a net win but there’s a real cost which is easy to ignore until you count the number of FTE weeks which went into keeping a project building and secure.
You're right that this doesn't represent the way a vanilla JS UI would ever actually be written in practice (even calling it a "vanilla JS UI" instead of an "html UI" points directly at the problem)
But:
1) Even though the web-specific example is contrived, it's a good demonstration/explanation of what people mean generally by declarative vs imperative
2) I don't want to wade into the tired argument, but classic HTML + minimal hand-written JS doesn't scale beyond a certain point. If it did, everyone would still be doing things that way
If you need to achieve the same functionality and maintainability without React (or some other external library), then by definition you somehow have to reimplement much of it yourself. And no, React isn't all that big in terms of functionality or size.
What are the chances that you are arriving at a solution that is better than React? That other engineers have no trouble understanding and maintaining? You'd have to invent new concepts, or at least call them differently, to not copy React's (or any other framerwork's) concepts or ideas. That would make it harder for other people to understand the code.
funny you should say that, because there is no explicit state here, what happens when there are 10 checkboxes? Just read `.checked` in the dom element?
Usually for things like that I'll come up with a CSS class that means "set it up so this element has its class toggled based on the state if the checkbox it contains". Sometimes I'll use data- attributes for additional configuration.
I think there are two ways of looking at things: the DOM is the view layer, created as a function of the state (React and things like that), or the DOM is the state and the view part is handled part by the DOM and part by the CSS. CSS already automatically creates the view as a function of the DOM.
you may think they're equal but to me one of the two is fundamentally flawed. I speak from the experience of developing for Android and web, but I suspect in all UI platforms, it's best practice to have the source of truth out of the views and treat states in the native views (checked) as derivative throwaways
I don't think they are equal. On the web, the "react approach" is seems to be really good if not the best for medium-complexity apps. Under that, it creates more complexity than it solves and over that it can affect the performance too much. The thing is, we work in a industry that has to deal a lot with change. Using the "react approach" from the beginning when your website is not that complex can be a way to future-proof it. But you're going to pay a cost of increased complexity at the beginning. I think that's the opposite of technical debt in a way. Technical investment maybe?
There is no cost, these are just 2 diferent styles of code, there is no reason for one way to be more expensive than the other. If you find react cumbersome then find a better way, a library, or vanila. Just because you dont do react doesnt mean you should write vanilla js in a spagetti manner. The "react approach" is only awkward if you fixated on the mindset that there is something special about "the platform", that you should write code in such a way that spread the platform dependencies all over the place.
In all of my programming exp, back-end or front-end, "the platform" is what people try to restrict access to and abstract away so that they can think about the problems they're solving. Only on the web, after decades of putting up with a then crappy platform, now better, that developers now have a Stockholm syndrome for it.
There is a cost though, the browser already knows HTML so using it for state has no cost, while adding something in pure JS does. For example, look at payload sizes.
> In all of my programming exp, back-end or front-end, "the platform" is what people try to restrict access to and abstract away so that they can think about the problems they're solving.
You're assuming that writing code for your abstraction will be faster than server-side rendered HTML with a sprinkle of JS. Sometimes it is, sometimes it's not.
> You're assuming that writing code for your abstraction will be faster
Strawman, I didn't even say "fast" at any point, there are many other reasons platform should be abstracted away: better visibility of platform dependencies, easier security audit, portablity, ease of refactoring/deprecation of platform APIs
If writing code for your abstraction isn't faster and the resulting code isn't faster, then surely not using it is useful sometimes? "better visibility of platform dependencies, easier security audit, portablity, ease of refactoring/deprecation of platform APIs" don't really matter that much for web browsers if you're not using the latest new shiny things, the backbone of the web is really stable.
I guess to me the reason is that I want to deconstruct problems properly and focusing on thinking inside the domain of the problem. It helps me think by pushing all the platform junk out to the edge.
I initially just mimicked the React version into Vanilla, so this was a show-off how to do that somewhat exactly the same way. But in this particular case it serves no meaning, we can always read 'checked' from DOM.
If the custom DOM element is deleted, then its entry in the map will also be deleted. Otherwise it would hang around in the Map forever (unless manually deleted in a lifecycle callback).
This was just taking the final React code in the article and rewriting it in vanilla as close as possible.
Your comment is fully correct, but I would like to point out:
- with such a tiny DOM to rerender, it is equivalent, probably even faster
- custom element encapsulates DOM and it is fairly trivial and extremely fast to pick DOM (like you can even use id's everywhere) in the 'old jquery' way, with simple library functions
- updating DOM with simple render() call is way more elegant, but if you keep custom elements DOM small (and one should), this way is not that bad at all
Like this:
<!doctype html>
<body>
<custom-element></custom-element>
<script>
const CHECKBOX_ID = "my-checkbox";
const defaultLabelContent = "Toggle me, you newbies";
const beforeDiscountText = "You have not availabled discount";
const afterDiscountText = "Discount Availed!";
const beforeLabelText = "Click on me to remove fake discount"
const afterLabelText = "Click me to apply fake discount!"
const state = new WeakMap();
// 'library' code
function qs(selector) {
return this.shadowRoot.querySelector(selector);
}
function getElem(nodeOrSelector) {
return nodeOrSelector === String(nodeOrSelector) ? qs.call(this, nodeOrSelector) : nodeOrSelector;
}
function replaceText(nodeOrSelector, text) {
let elem = getElem.call(this, nodeOrSelector);
if (elem) elem.textContent = text;
}
function updateAttribute(nodeOrSelector, name, value = '') {
let elem = getElem.call(this, nodeOrSelector);
if (elem) elem[(value ? 'set' : 'remove') + 'Attribute'](name, value);
}
// end of 'library' code
class CustomElement extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<input type="checkbox" id=${CHECKBOX_ID}>
<label for=${CHECKBOX_ID}></label>
<div></div>
`;
this.shadowRoot.addEventListener('change', (event) => {
state.set(this, event.target.checked);
this.update();
})
this.update();
}
update() {
let isChecked = state.get(this);
updateAttribute.call(this, 'input', 'checked', isChecked);
replaceText.call(this, 'label', isChecked ? beforeLabelText : afterLabelText);
replaceText.call(this, 'div', isChecked ? afterDiscountText : beforeDiscountText);
}
}
customElements.define('custom-element', CustomElement);
</script>
I love React. I love the elegance and flexibility of it's architecture, and the power of it's modularity. There are so many really great reasons to use React.
This article is absolute nonsense from start to finish. What amounts to basically "lying" about the complexity of vanilla JS actually ends up mainly just doing a massive disservice to React which shouldn't need false comparisons to show it's benefits.
Maybe they're just being disengenuous but I got the distinct impression from many of the examples that the author doesn't actually know vanilla JS at all.
This is the impression I got. The first half of the vanilla example is imperatively creating DOM elements, assigning attributes, and inserting them into an “app” <div>. My guess is the author learned React first and never considered that HTML can be written as (gasp) HTML!
With react you've just moved your imperative code from REST->DOM manipulation to REST->local state manipulation and local state -> DOM mapping.
You have to invent some elaborate local state schema and then map it to DOM, instead of keeping part of the state implicitly stored in DOM, where it makes sense.
You can assign your data directly to DOM node objects. Say `el.yourData = todo_item`. And then let the order and list of items be kept implicitly in DOM, so you can do obvious things like remove nodes using `.remove()` reorder them via DnD, and whenever you need the list as data, you just querySelectorAll('.item').map(el => el.yourData) and you have your list. All quite straightforward.
You can also do context dependent things by being able to travel up to the ancestors via parentNode.
Declarative frameworks like react make simple things like this painful in comparison.
The problem with React (or the reason you might want to sometimes choose VanillaJS) is that React's algorithm to apply a DOM diff has to be completely generic. In React your flow will loosely be
Something happens
The browser triggers and event and sends it to React
React calls your code to re-render the world[1]
React takes the new world order and does a diff
React calculates what to do go from old -> new and applies
In VanillaJS world you can do way way way better than this with the benefit of knowing how your application works.
Something happens
The browser triggers and event and sends it to your handler
Your handler updates the DOM
It would be nice if in future you could make some promises to React about what happens on an event so React can skip the diffing step and just update the virtual and real DOM directly. And hint hint if you can generate the code to update the DOM directly in the general case too then do you even need the virtual DOM? Svelte is doing really cool work in this space.
> The whole point of react is to avoid the stateful nightmare created by the latter approach.
I found this ironic, as I have never in my life had more struggles with state, than when working with react native. Seriously, the amount of times something didn't update, or was updating way too many times, is countless. It's so easy to shoot yourself in the foot. Imperative native programing, almost never have this issue.
You could try angular then, though at some point you're going to run into a similar problem: your zone not becoming stable (mostly a problem when writing tests) because of some external dependency using a setTimeout or setInterval*. But at least it's generally pretty fast at updating and works without shoehorning you into one way of managing state.
Overall I prefer React because I prefer simple composable libraries over frameworks and Angular reminds me too much of programming in Java with its long application boot times and its folders full of ListUpdateCheckerConditionFactoryBeanUnitTest.java's.
*To be fair, in an equivalent React app, at that point you would have had to write a whole lot of shouldComponentUpdate to not tank performance.
Of course! The takeaway is that in the future it would be nice if you could have the best of both worlds by being able to compile some DOM transformations ahead of time and/or have React observe and JIT them so you can bypass React's machinery most of the time while getting to pretend its there. It will feel like a re-render but it's actually just a direct DOM manipulation behind the scenes. That's Svelte in a nutshell.
Solid seems awesome. Though as a Svelte dev, the jsx / React flavored api felt like a regression from the simplicity and readability of Svelte’s template syntax. Have you explored both in depth? I’d be curious to know Solid and its ecosystem compared to Svelte and Sveltekit .
The point is that in real applications it is really hard to get the update logic straight.
The React haters here seem to think that is easy. But most of the popular frameworks/libraries use a virtual DOM in some way or another. And Svelte also automates handling the updating. Maybe it's not so easy after all, if smart people have spent decades trying to find alternatives.
The beauty of the virtual DOM is that it relies on pure view functions: given this props+state input, this is how the DOM looks like. Generating DOM fragments on update is possible, but naively done it will quickly get out of sync with the DOM view function. Curious about 'really cool work' in this space!
> Generating DOM fragments on update is possible, but naively done it will quickly get out of sync with the DOM view function. Curious about 'really cool work' in this space!
This is what Svelte's doing, as I understand it: letting you think like you're writing React ("when in this state, the world should look like this") but avoiding the (greatly overstated, IMO) overhead of the virtual DOM by translating the changes into direct DOM updates in a complilation step.
Virtual DOM overhead is only overstated in that it usually isn’t really compared to any reasonable baseline when discussed. Many will acknowledge that React was faster than many of its contemporaries—and I’m sure it remains competitive—but still express concern over the unnecessary overhead of the virtual DOM.
I think I blame part of this on the language we use. These days a huge buzzword of sorts is the “zero-cost abstraction.” It means zero runtime cost, but a lot of us internalize it as completely free, failing to account for the fact that it often entails increased complexity and build times. Granted, often this is a worthwhile tradeoff, but in order to know you need a more nuanced comparison. That virtual DOM is “pure overhead” isn’t objective fact - pure overhead is an error of coding that can be fixed in a pull request. No, this is more like ideology.
The problem with this ideology isn’t that it’s bad, it’s just incomplete. It usually explains away why the world hasn’t simply adopted their worldview by appealing to ignorance or even stupidity, while ignoring potentially serious issues. Like yeah, if I want to make a desktop application in 2021, I have to consider Electron because simply put, there’s a lot broken in modern native UI.
And sometimes people are still right. There’s always a possibility Svelte will win out, or eventually be proven right even if it doesn’t.
Maybe. But it’s 2021, and the year of the Linux desktop is still around the corner. If something hasn’t happened yet, at least humor some reasons why that might be the case.
(Personally, in my experience, even update-intense apps wind up having bigger fish to fry than VDOM, so I can’t say I’m holding my breath.)
In practice, React users often end up running all their app code through a big slow compiler anyway. Usually Babel (to convert JSX to createElement calls) or TypeScript. Svelte doesn't increase the overall complexity of your build process, it just uses that compile step to do something to your code that's actually useful.
Adding more compilation steps is not free, though. You have to convince people to want this. It's not necessarily an easy sell.
Even though JSX is pretty React-specific, it still has managed to make its way into other frameworks and many compilers because it's fairly general at its core and because it is a fairly non-invasive process. So for example, you can compile JSX using babel, but also TypeScript, esbuild, and more. (And those are all independent implementations!)
I can't claim to know what the build process looks like for Svelte, but I have a suspicion it is far less general than the source code transformations that we have so far that provide type checking and newer ES functionality in older engines. (Which, by the way, you appear to be insinuating are not "actually useful.") Angular has a similar thing going on and it is not terribly well received.
Virtual DOM overhead is totally overstated. It's just that it offends some people's idea of efficiency to build a data structure to throw most of it away anyway.
But in reality, the virtual DOM allows you to avoid manipulating the actual DOM, which is both slow and error-prone. The browser necessarily attaches all sorts of memory objects and state to components in the DOM, which his why unnecessary mutating has to be avoided at almost all cost.
Svelte may go a step further by automating DOM manipulation. But it doesn't have the same following yet. And usually, if there is no explosive growth in such a technology, it's not just because of evil overlords preventing its justful rise...
vdom has the potential to do optimizations that simply aren't possible in individual functions because it sees the whole DOM at one time.
There's also the preact method which diffs against the DOM directly, so that overhead goes away.
Do you remember when InfernoJS broke the framework test? It blew away 2-3 versions of vanillaJS with it's vdom implementation. They literally tore apart what it was doing in order to finally create a vanilla version that was faster, but very unmaintainable.
Today, "native" frameworks like Svelte are only fractionally faster at synthetics. At the same time, each component contains a brand new set of functions which has two major drawbacks. First, code size grows at F x N (where F is framework size and N is your normal code) rather than F + N. Linked to this is the performance implications. Once the JIT warms up the vDOM code (almost instantly), the user gets max performance for the most critical loops when visiting pages for the first time. With "native" frameworks, each new page means switching back to completely unoptimized code.
It is theoretically the case, that a virtual DOM implementation has to do more work than a perfectly efficient manual (or automatically generated) DOM mutation.
However, in practice the difference hardly matters and it is really hard to do efficient DOM mutation correctly. Which is why virtual DOM approaches are at the heart of most of the modern frameworks...
They are at the heart because it allows people to write code faster while being OK with the perf/mem hit. But in the end its always less efficient which is why you have to hand wave it away.
Like I did in another subthread I’d suggest looking at SolidJS. It’s philosophically similar to React for rendering, uses JSX, and at least as “pure”. It has reactive APIs similar in use to React hooks (neither of which are pure), but compiles to DOM.
Why would you use JavaScript to write your HTML elements in the vanilla case? Surely you could just have a plain HTML checkbox and your default text, and then add an event handler to update the text when the checkbox changes? That’s how React with something like NextJS behaves, vanilla JS you get it for free unless you decide to use JS to write your elements like this.
I think that was mostly for illustrative purposes. Most websites aren’t built like that, but it helps show how attributes on DOM objects can be manipulated before throwing the reader into React state concepts.
To me, the more fundamental problem is the fact that we spread state across multiple computers.
If you only mutate state on the server and simply send views back down to the clients, nearly all of "modern" web development practices can be safely ignored.
Clearly, there are difficult engineering challenges with the approach of "all state on the server all the time", but anything worthwhile is never easy. Down this path you might not obtain Netflix-tier webscale directly out of the box, but that doesn't mean we throw our hands up and have all our clients drown in multiple megabytes of angular11+ dependency trash either.
How many internet users are more than 50ms away from a Cloudflare/Microsoft/Google/Netflix CDN in 2021? How many cores can you get in a 1U server now? Distributed state machines for client UIs have been a massive mistake.
I mostly agree, but having used Linear.app which has a crazy good and fast UI based on a local database synced with their cloud database, I'm not sure anymore: https://youtu.be/WxK11RsLqp4?t=2170.
I think it's important to define early in a project if the app is "cloud-based" or "local-based". If it's cloud-based, then only the server mutates state and sends views to the clients, as you wrote. Everything is simple. If it's local-based, then all data are available locally and mutated locally. The cloud is only used as mirror/backup and can support for some advanced features like full-text search. That's also a very clear architecture. That's how Linear works.
Things become hairy and unnecessarily complex when no clear choice is made between those two options, then it's not clear where the state is.
> there are difficult engineering challenges with the approach of "all state on the server all the time"
There are real world challenges. There's no internet access on the NYC subway. Latency can spike for no apparent reason. Making a webapp a dumb client really only works for wired connections on an intranet.
When I was working in South Korea, their subway system had excellent reception to cellular and even satellite TV networks. They explicitly engineered a solution to this problem in direct, first order terms.
This is precisely the type of approach we should be taking in my opinion. Make the networks robust, not the client-facing web apps. We are spending engineering resources in largely the wrong places.
This would be like arguing for a car that can hover, but only for an ambiguous/non-specific amount of time, simply because the roads are occasionally total shit.
But it's two very different groups of making those decisions.
I try my best to build robust apps but have no bearing on my countries infrastructure plans. And have the government reallocate my resource to setting up antennae in tunnels is dystopian.
> To me, the more fundamental problem is the fact that we spread state across multiple computers.
I completely agree. Sharing a layer of an application between client and server is a nightmare and will fly in your face.
That said, it is possible to cleanly handle more involved interaction on the FE and have a clean separation of responsibilities. When well done, the results are very snappy and robust. Of course, it isn't always necessary, and most RoR applications beat that 5mb Angular behemoth.
I wish software developers would be more honest about their tools, weighing their pros and cons, instead of thinking up contrived 'examples' that paint them in the best possible light.
Every library/tool/framework will make you think "well, this sucks" at some point. So be honest.
I tend to see the problem as the point where people stop thinking of them as tools and more part of their self-identity. If learning how to use React makes you more productive, it’s only reasonable to use it on your next project. The problem starts when you start thinking “using React makes me a Real Programmer™ like the cool kids at Facebook” and that turns every discussion into a tribal loyalty test.
I get the impression that the author is so into the react way of doing things that they can't seem to comprehend and write JS in a non-react (vanilla) way, thus polluting the author's approach to writing vanilla JS.
If anything, this article is an example of how you need to write and truly understand vanilla JS (and learn form your mistakes) before you can appreciate and understand what react had to offer when it first came out.
I might have to write a counter blog post just to balance this out...SMH.
The paucity of Facebook veterans does more in my mind to explain the popularity of React than its UI state model. React is popular because it's popular because it's popular ...
I remember using jQuery, Backbone/underscore, handlebars, knockout, Angular 1, Enyo, Ember, and probably a couple others. Angular in particular had like 70% market share among major frameworks and almost all the libraries didn't support React.
React took over because it was head and shoulders better than everything that went before and taking days to learn instead of weeks or even months.
The reason nothing has taken over since is because we've reached the point where they aren't good enough to justify throwing away all the existing library support.
No, they get instantly hired, then they say nothing but "React ... React ... React." And the rest of the team goes, "wow they really do things differently in Menlo Park!"
I like most of React and am strongly against writing anything akin to an SPA in vanilla JS. (Whether or not an SPA is the right approach is debatable but besides the point)
And yet this article is disingenuous. The OPs target can be achieved using CSS alone. In fact, quite a lot of things can be done quite well in vanilla JS and CSS.
This works well to some degree. Then I found it to fall apart.
- Those BE devs now forced to write FE in React|Vue|* and hate it so much that they've never learned it, won't learn a vanilla stack either. Especially not CSS. That's just for colours. Or something.
- Most vanilla approaches I worked on relied heavily on the correct ordering of HTML elements with the correct classes for something to work. There's little to no encapsulation. Only copy and paste. It's also immensely brittle.
- You can add some simple encapsulation, but the easier way is to brute force some imperative mess. Close ticket. Open next. Velocity is high.
- The next step is to build your own framework. But chances are it will be worse than any 3rd party solution.
- A huge advantage of 3rd party libs is that there's zero chance of some ticket specific code to erroneously make its way in to the framework. Silos aren't always bad.
- Onboarding new devs is hard. There's probably no resources at hand.
- At this point you've tied yourself in to knots and the actual problem, your product, isn't even close to being solved.
It takes less than an hour to have a fast React|Vue|* application off the ground. Everything you need to build the product is in place. And with some care, it will remain fast. Alternatively, Ruby on Rails and friends work just as well.
However, wasting endless hours reinventing a worse wheel is unproductive.
Thanks for the recommendation! Read through all the three articles. Learnt a lot. Even though some of the things written in a way that were 'handwaving' as the author writes himself, and I couldn't understand everything ( I'll keep revisiting ) I'd never thought about declarative model enabling using O(n) code instead of O(n^2) when state in the application increases, and some of the other things he talks about. I've linked these articles series as a first thing in my blog post.
Toggling the checkbox in the completed vanilla example halfway down the page didn't change its subtitle text for me on Safari - instead it updated the subtitle in the example at the very top of the page.
Does it find the first matching element on the page and update it? (I don't know JS, DOM)
Let's not forget that if your main goal is to have an element that is clickable and update it's label you can write a function for that or even create your own component that does just that.
Your heard me correctly, you can create components with JavaScript without the aide of a framework.
Showing the difference between markup/scripting a single static element is pretty different from applications where you have to display lists of things with other lists of things inside. If you only have static elements then just writing it in HTML and making it dynamic with JavaScript is fine.
But once you have to display lists of stuff with lists of other stuff inside you basically need a template library otherwise you're going to be doing some pretty weird DOM manipulation to copy sections of elements.
So a comparison that might resonate better with folks here might be to have a pure JS TODO app with a comment section in each list item. It becomes harder to express in pure HTML. You'll want some template library to keep it maintainable.
If your lists are long enough, you probably want to split what you can actually display from the rest of the list, because even today updating the DOM with thousands of nodes is expensive. And doing seems to be easier and less chatty to do on the front-end I think.
Sure templates on the server are fine too I just mean that the comparison is more compelling when describing the things that prompt templates in the first place (whether on the server or frontend).
Over engineering things is truly one of the biggest problems in our industry. It's been years and react still seems like an organized religious community to me. With many, many preachers.
Reminds me of all the "yaml is better than XML/JSON" examples where everything is carefully formatted in a way that make no sense unless you are trying to prove a point.
Part of me think we should just ignore html and just render everything using webgl/webvulkan/whatever instead of fighting the browser's flow. We're not there yet but things have been moving in that direction because it kind of makes sense
Yeah, they don't but perhaps the necessary meta could be generated as things are being rendered. It's certainly possible, though I haven't done any actual work on accessibility tools myself. I guess it's not a trivial problem to solve without breaking things here and there, otherwise someone would've done it already. Not impossible I'd think
Huh. This OC frames the debate as imperative vs declarative. Is that right? It just now occurs to me that React vs vanilla is another iteration of the MVC debate.
I lived thru the Java AWT to Swing transition. One of Swing's Big Ideas was for every component (aka view & controller) to be backed by a separate model, finally mainstreaming the MVC design pattern.
Then of course all the web frameworks got nutty over MVC.
My only takeaway from the many, many MVC debates is that the only way to win is to not play.
While this article doesn't do a good job of showing the benefits of state model, I still think it's a great way to build ui. Thinking of ui as derived state from your application model really helps in scaling ui applications reliably. Unfortunately react is not the best example of this approach. I would say try out elm or some of the clojurescript frameworks like reframe to truly grasp ui as application state
Sometimes I think people don't know for what purpose projects like React are developed. Or they act like they don't know. Please stop praising your horrible code written in vanilla javascript.
Just put the checkbox html & css on the page. Write a function that updates the label based on checked state. Call it on input/change events.
It's like 3 lines of js.
React.js people always invent this vanilla js / jQuery strawman that's so complicated and spaghetti that no mortal can possibly understand it.
It's total BS. You can write succinct maintainable code without a 100 lb framework.