Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Totally disagree with this.

Change detection is nothing but a hack to get around the fact that interacting directly with the browser DOM is very slow and blinky.

Imagine a world where interacting directly with the browser DOM didn't suck, then none of these libraries would exist.

The crux of the problem is that the browser immediately reflects changes to the DOM to the screen. And when you make a bunch of changes to the DOM you immediately see a bunch of changes happen, with portions of the screen being blanked out, and layout changes happening, and a whole bunch of other nasty stuff happening immediately, which all sucks. And updating a physical screen is slow too, double sucky. Change detection is a technique for minimizing updates to the DOM to avoid the suckiness.

I implemented word processors, and I've used a technique called 'double buffering' to rerender complete pages off-screen, fast. And then update the screen smoothly. If we used a proper word processor engine to display our UIs instead of a browser engine then we'd have no need for React etc... After decades, Chrome finally has a rational equivalent to this technique called the View Transitions API.

My hope is that the days of doing change detection are numbered and we can finally leave all this change detection crap behind us.



> the fact that interacting directly with the browser DOM is very slow and blinky.

This is not really true, or at least hasn't been for many years.

Change detection's primary purpose is to let other code know when something has changed, and what changed. You could theoretically re-render all your DOM based on that information, and in many cases that works much better than people expect. But the DOM is stateful, and truly large trees of 1000s of nodes are too slow to update, so frameworks try to also do minimal DOM updates.

> The crux of the problem is that the browser immediately reflects changes to the DOM to the screen. And when you make a bunch of changes to the DOM you immediately see a bunch of changes happen, with portions of the screen being blanked out, and layout changes happening, and a whole bunch of other nasty stuff happening immediately, which all sucks

This is also not really true. The browser doesn't update the screen until an animation frame. Anything you do synchronously is not rendered to the screen until code yields to the event loop. Even many async things, done on the microtask queue, block the next animation frame giving us visually atomic updates.

Lit, for instance, uses this to get async, batching rendering without unintended partial renders of the screen.


I don't see it that way. The main need for change detection in my opinion is to remove the need to update parts of the DOM in an imperative manner. It is fine to do that for smaller projects but when a project gets large, it becomes difficult to reason about the myriad of changes happening without a system to handle that. I find any one of the examples in the linked post way more easy to reason about than manual DOM updates.


Having worked extensively with the major frameworks in multiple orgs, I have largely found that templates synced with state via change detection (like Angular) result in more readable/maintainable projects.

While I personally like React, it's pretty tricky to use in reality. I have found that its idiosyncrasies make it less resilient to suboptimal contributions from engineers that are still early in their careers.

Broadly, the projects I have entered using React tend to be pretty wild, messy and unreliable.

There is cross over though, Angular projects using Redux (ngrx) are horrific. The criticism of Angular for me is the lack of control you have over the compiler, dependency versions and testing strategy.

Svelte and Vue are both interesting, but they use custom file formats (.vue, .svelte) which can add an extra layer of unreliability to projects using TypeScript and testing frameworks.

The advantage of Angular is that it's an ergonomic abstraction that allows optimisations to occur at the engine level. It's more like an ORM, where React is more like writing SQL queries directly.


I’ve only ever touched React as a “so and so needs a hand on this bug” or “we want this feature added to legacy while the React team works on the new version”, so I was already going into a codebase that had long been abandoned in all but usage.

That said, it was a nightmare. I think we had state managed by vuex, and I just needed to add a new data point to the component and display it. But the data needed to be added to state…so I think I touched 6-7 files, adding mutators and updating definitions etc.

The experience put me off of React, though I do imagine a lot of that comes down to how the code is initially structured and maintained. It’s clear that whatever they were doing was more “make it work now” and it so much worrying about the future.


Have you tried the model view update pattern?

If you haven't I recommend having a look. If found it amazing for maintainability.

You have to be carful to structure everything well. If not you blow up the number of messages in one file. But once you get past that it's really amazing.


Yeah I have, personally I like it but the issue I have found was that I am not the only one who works on projects.

> You have to be carful to structure everything well.

This essentially sums it up. I can be careful, but it's impractical to review code to ensure it's also structured correctly when working on a team with lots of contributors with varying experience levels.

In my experience, It's also more difficult to teach MVU than it is to teach templating with automatic reactivity.


Sadly, I have to agree with everything that you've written there.

For simple use cases templating is much better as well. It's only in complex state management that I find MVU is worth it.


I agree with you that going back to the JQuery days of unmaintainable imperative code would not be a good thing.

But with JQuery I was able to create adaptive UIs by applying 'transforms' to the DOM, I really miss that. Think of the way that 'tag helpers' can apply cross-cutting transforms to Razor Page applications, I used to do things like that with JQuery.

React et al cuts me off from both the browser DOM and it's own virtual DOM, so this I can no longer do this. I have to do everything the React way. Developing with React is far less powerful than JQuery. I still find modern frameworks to be a kind of straight-jacket. I hope to have my transforms back in a rational way someday.


React discourages direct DOM access, but it’s still possible.

https://react.dev/learn/escape-hatches


It seems like you are a ripe candidate for Svelte !


Engineers developing rendering engines for browsers definitely know what double buffering is. However, the accumulation of peculiar layout rules, support for CSS, and many other things make things hard. One of the major design flaws of the DOM was allowing unrestricted mutability. If DOM mutations were transactional by default, it would offer better control over layout reflows.

Change detection remains a common UI issue, even in native UIs. While rerendering everything on each UI loop is possible, as complexity increases, optimizations involve detecting changes. Smalltalk introduced MVC to identify changes and update the UI. Java Swing and Qt also adopted similar mechanisms with different names (observers, signals, and slots). Apple's Cocoa employs a variant of MVC as well. Change detection has historically been a common problem even in Win APIs.


I don't know... even if the DOM was super fast, would it really be ergonomic to keep all your state in the DOM tree and only work with that?

I kinda doubt it.

So then you'd have to store some state in JS, and some in the DOM, and again you get a syncing problem, since you lost your single source of truth.

Or did I misunderstand your comment?


At peak jquery, I argued successfully for storing the metadata on DOM, because then you have a system of record, upon which you can build a straightforward source of truth. React does a ton of work to solve that architectural puzzle another way.

The thing I have run into over and over again is that when we try to pretend the system is Y when it is in fact X, inevitably the impedance mismatch results in 1) bugs that should be easy to reason about but are difficult to fix, and 2) a form of pulling the ladder up behind you.

If I want to continue to work on interesting things I have to be able to carve out chunks of my code to gift to #3 on the bus number list, so I have the bandwidth to double down on some other topic or expand into another.

I know which side my bread is buttered on. Many don’t. Which is why I have a more consistent supply of mentees than many of my peers.


Data comes from the server as JSON. Data comes from the user as DOM events and changes. There's always going to be an impedance mismatch.

The DOM API sucks, so React et al went all in with state-as-objects. IMO it was the right call.


I just don't think DOM->JSON->server->microservice->DB->microservice->server->JSON->DOM is sustainable long term. It's had a good run, but it's a big part of the reason I'm looking at Phoenix for a personal project. React is predicated upon solving the wrong problem.

Talk about impedance mismatches.


I'm not sure switching to DOM->JSON->server->microservice->DB->microservice->server->DOM will make much of a difference.


Complexity is usually quadratic or factorial. Only a tiny number of systems are logarithmic. 20% reduction in parts can often be a 40-70% reduction in cognitive load.

But the funny thing with fractions is that if go above a threshold and come back from the brink, the path back is a smaller number than the path out. So if you go from 8 to 10 steps that's 25% worse and if you undo that bad decision you are getting 20% better. So you want to be careful to compare sustainable to unsustainable and not the other way around, if you follow me.


> I argued successfully for storing the metadata on DOM

Who did you convince?


A team working on an AJAX webapp that typically was meant to comfortable manage around 1000 blocks of state at a time. Somewhere on the order of 6-10 pieces of data per block (Which we just changed as a unit of work because 1000 was already hard enough). And when you're dealing with 10^3 you need a couple of different ways to visualize the same data or you're getting nowhere, so we had one or two other views that dealt with different levels of detail.


>> Or did I misunderstand your comment?

Well, I can see now that while I expressed my dislike for change detection I didn't paint a good picture of why I think we'd be better off without it :-).

While I appreciate React's declarative programming style I dislike the fact that React (and all the change-detecting alternatives) force me to interact with the DOM through their APIs.

JQuery allowed me to do whatever the hell I wanted to the DOM, but the resulting screens updates were very ugly. And yes, JQuery resulted in ugly and unmaintainable code but that's not on JQuery, there are rational ways to apply transforms to a DOM.

I want to "do whatever the hell I want" to the DOM because I want to apply cross-cutting, adaptive transforms to my application, like I could with JQuery. And I think that we'd all be better off for it. In the transition to React-like frameworks we have lost that ability

The view transition API lets me start a transition, do whatever the hell I want to the DOM, and then update the browser smoothly.

So what I'm imagining is a component-oriented framework like React that doesn't lock me into a component-only API. A framework that, when rendering occurs, starts a transition, let's me do whatever the hell I want to the DOM, and then updates the browser smoothly without doing change detection.

And the reason I would like that is because, while React's declarative, component-oriented development approach is nice and all, it's also very restrictive.

Reacts prevents me from directly traversing and mutating the DOM, and it's own virtual DOM, because if it did then it can't efficiently do change detection.

If we replace change detection with transitions then we can "do whatever the hell we want" (hopefully not in an unmaintainable JQuery way) and still have smoothly updating displays. We'd have a framework with all the benefits of React and without the restrictions.

Anyway, that's my best shot at explaining myself :-).


I‘ve written a UI a few months ago with only lithtml and direct DOM manipulation and it worked pretty well.

I needed to do calculations and reading bounding boxes from the DOM initially and on resize. Basically a hack to get around CSS limitations.

It was crucial that this is fast enough to avoid flickering.

I wouldn’t have attempted this with react. I think it would still be possible with useEffect, but I tried the most lightweight and fast by default approach that was feasible.

Have a look at lithtml.


in the cljs/reagent world anytime you need to do direct dom stuff you just use a ref - that must me the same for react proper?


You can pretty much do anything with a combination of refs, events and useEffect. It’s often fiddly and cumbersome though.

Aside:

That’s why I‘ve been gravitating towards approaches that have much simpler execution models.

You can still do declarative and functional. But you have more control when needed, create _much_ smaller bundles, have fewer dependencies, better default performance, allocate less garbage and you can reason about your code in more fundamental ways.

In cljs this would mean to not use a react wrapper.

However I personally try to avoid using any deps for frontend, and that includes cljs. Any dep needs a justification IMO.

That justification is often not good enough, since I’m mostly writing „interactive documents with some fancy parts“.

If you’re writing something like nextjournal, roam, data rabbit or similar then it’s a different story.


So what are you disagreeing with? Are you disagreeing that change detection is what mostly diffentiates the front-end frameworks? If so, what is that important factor that differentiates them if not the mechanism that modifies when change is detected? Why do you have such a strong belief that this factor isn't it?


They’re disagreeing with change detection being the most important part of a frontend framework by suggesting that the browser API change dramatically so that we fix the important problem of… change detection in frontend frameworks. Yeah it’s not the most consistent.


Change detection has nothing to do with the speed of DOM updates, it's about directly encoding dataflow dependencies to avoid code duplication and redundant UI updates. You could paper over the lack of dataflow updates by just updating the whole screen every time, but you still need to preserve various state, like scroll positions, highlighted text, etc. which still means only some state changes and other state does not.


Redundant UI updates could be okay if done quickly and cheaply. Games refresh entire framebuffers 60 times a second, redrawing the entire world, and it works fine, because they do that efficiently, and flip entire pages so that you never see a half-draw.


Full refreshes aren't cheap though, games consume a lot of power.


DOM wasn't meant for real GUI's and trying to trick it into being one seems a fool's errand. It was meant for static documents. DOM is the wrong tool for the job.

Time for a new state-ful GUI markup standard so we don't have to rely on the whacky DOM to get expected and common GUI idioms.

Maybe build the "engine" on top of the Tk or Qt kits to avoid starting from scratch.


That's not nearly all there is to it. If the DOM was fast and you could control redraws, it would still be horrible.

A performant big pile of mud is still a big ball of mud.


"A performant pile of mud" is a description that fits many things that still stay afloat exactly because the performance allows them to. Consider C++ or x64 as examples.


For example, there's the way it's still impossible to pass data between native Web Components without stringifying it first.


This is an absolutely stunningly false and ignorant statement. I can't believe it's still being repeated.

Web components are objects and they have properties that can be set. The entire web components community - which includes the developers of apps like Photoshop, Reddit, Chrome, Firefox, and a lot more - passes properties down through trees of web components _all the time_.


I reallly love web-components. They allow to encapsulate logic, and be re-used and composed with others. It starts feeling like unix in the browser, to me.

Maybe a bit verbose, but here is an example https://jsfiddle.net/8kvucm9f/5/ in the context of the blog post (and with my preferences of doing things; there are just many different ways of "writing web components", and can adapt many use cases).

Also, by using their HTML attributes (id="", name=""), it is possible to make really nice features with CSS.

Finally, they can offer a clear API (just like the <img/> or <video/> or <input/> elements), to the users of these elements.

They can be inserted, to give "markdown" documents (or .org mode etc. if it is converted to HTML), JSX like features, by being "just embedded HTML text".

It is super powerful, maybe feels a bit scary at first, but all problems seems to get solvable with clean patterns.


There is a subtle grain of truth to it - the implied "declaratively" here. React has went south quite a bit, but the basic idea of JSX where HTML becomes a language with first class objects, functions and bindings is extremely compelling. It offers a solution to the problems of complexity that pure HTML cannot tackle while still staying somewhat true to its declarative nature and reaping most of those benefits (instead of fully switching to the imperative DOM paradigm).


Adding "declaratively" doesn't make it true either.

You can declaratively pass objects between web components in every web components library out there like Lit or Stencil.

Now, you may claim that that doesn't count because the declarative part is implemented in non-standard userland libraries, to which I would reply:

1) So what? React is a non-standard userland library. You can't pass data declaratively between React components without React. At least with web components there's a standard _interface_ for the components so that many different libraries can implement the declarative parts in a completely interoperable way.

2) Yes, you can't pass objects around in plain HTML because plain HTML doesn't have references to the objects in the first place. Even if you did add a way to set properties in HTML, what would you set them to?

2b) This is actually likely changing as the Template Instantiation proposal moves along and possibly gets the ability to make property bindings. This will be useful for declaring custom elements that accept properties. But the question remains: where do those properties originate from? Most likely JavaScript, because HTML doesn't have any.


> You can declaratively pass objects between web components in every web components library out there like Lit or Stencil.

They capitalized Web Components, I think they're talking about what browsers natively support and not a library or framework.


> You can declaratively pass objects between web components in every web components library out there like Lit or Stencil.

This makes it a non-standard library-specific method of passing things.

I have no idea what you're arguing against.

There's literally no way to pass objects as attributes to web components. Web components had a great opportunity to get rid of attributes/properties dichotomy, but they doubled down on them.

> At least with web components there's a standard _interface_ for the components so that many different libraries can implement the declarative parts in a completely interoperable way.

Of course there's nothing standardised, and nothing interoperable. Because if one web component exposes its API through attributes, and the other one through properties, and a third one through both, never the three shall meet. Without library-specific hacks.

There's a reason the "standardized" lit uses a custom DSL to distinguish between things:

  `<some-component 
     attribute="string only" 
     ?boolean=${boolean}
     .property=${any_js_variable}
     @event_name=${callback_function}
  >`
So much standardization.

Edit:

Stencil uses JSX (non-standard), so it doesn't care and probably sets properties directly, skipping attributes. For object props and HTML it says it explicitly that you cannot set them (of course): https://stenciljs.com/docs/properties#object-props

> But the question remains: where do those properties originate from? Most likely JavaScript, because HTML doesn't have any.

Indeed. That is another issue with anything web-component-related. Moar Javascript. As if we don't have enough. There are now dozens of more JS APIs existing just because of web components, and even more are incoming: https://w3c.github.io/webcomponents-cg/2022.html


> Of course there's nothing standardised, and nothing interoperable.

This is so very much not true. On what basis do you even claim this?

Web components are JavaScript objects that extend from HTMLElement. They all share a common interface of attributes, children, events and properties.

You can get a reference to any web components any set a property on it:

    document.querySelector('my-element').someProp = {};
This works on all web components. It's interoperable because there's no special React, or Angular, or Vue version of an API to set properties.

> There's literally no way to pass objects as attributes to web components

How do you even get a reference to an object in HTML to pass as an attribute, if you could?

And who cares? You can pass objects as properties to web components. Attributes are string-valued, why would you want to pass an object to them?


> This is so very much not true. On what basis do you even claim this?

I worte the basis, and you completely ignored it.

> And who cares?

Ah yes. The mantra of all web-component zealots. "Who cares".

How it started [1]: "we have too much Javascript, we rely on too much Javascript for all our functionality, empty body tag is the bane of web sites" etc.

How it's going: Who cares? Dozens of new JS APIs! More JS APIs coming every month. Why would you want to do anything with web components outside of Javascript? Declarative? Oh, in any of the Javascript libraries. Javascript Javascript Javascript Javascript Javascript.

> Attributes are string-valued, why would you want to pass an object to them?

That is not what anyone wrote. Literally not a single person.

Anyway, this is hopeless. Web Component zealots are absolutely deaf and blind to anything outside of their world where goals shift faster than grains of sand in a hurricane.

[1] The original pitch for Web Components: https://fronteers.nl/congres/2011/sessions/web-components-an...


You started by saying it's false, then proceeded to write something unrelated.

If you start with such a statement, it would be best to follow up with proof instead of something unrelated.


Not via html attributes. But it's never been a problem to pass data via JS properties:

   someWebComponent.data = { foo: "bar" }


I think your idea of speeding up the DOM is the right one, but the view transitions API has almost nothing to do with it. That’s more for whole page changes, and doesn’t solve the main issues which is that the DOM simply does way too much and is bloated to all hell, and that JS is single threaded.

I’d like to see a new mode introduced ala “use strict”. I know the big brains at the top hate this but we need a way to shake off some legacy especially in the DOM. A few changes to its semantics could dramatically speed it up. And then the second part would be a way to share events and DOM across workers so we can put React essentially off the main thread. These two changes together would make websites able to feel like native apps.


> DOM simply does way too much and is bloated to all hell,

It's my observation that React and similar frameworks really help to bloat the DOM.

Now someone will say it is how people misuse React and similar frameworks.

and then I will say if large parts of the population using a technology make the same misuse of it, even people who know better, it seems like the technology supports that misuse and is to blame somehow.


I don’t agree at all, I don’t see any extra DOM due to React across many apps nor any fundamental reason why it’d cause it.


DOM changes are only a part of the story. Another part is a component model that allows to build complex software from tiny and sometimes messy individual pieces.


You want to store your application state in a widget tree? This was the part about the old bit 'o jquery approach that drove me absolutely batshit. The logical state of your UI is strewn about in Dom objects and their listeners, closures etc. There is no one place you can go to see what state is relevant to this component.


With the exception of list comprehension, which is IMO not only the best feature of jquery but almost enough of a reason to use it on its own, browsers have copied most of the features at this point. Do you (or anyone else here) see an opportunity to copy features from React into the browser?

I know there have been earlier tricks to pop elements off the DOM, modify them heavily and then pop them back on. I wonder if it would be better to a) provide a way to mark a node and it’s children as suspended reflow, or b) provide a virtual view where you can do operations on off-DOM nodes including ones that typically trigger reflow. c) something else entirely


Change detection is one thing, and double buffering is another. Change detection is just projecting from state changes to UI changes. It doesn't try to solve this hypothetical "flashing". That is entirely up to the browser. Double buffering is a graphic technique meant to show smooth UI changes at the cost of doubling the memory used for the graphical object.


> I implemented word processors, and I've used a technique called 'double buffering' to rerender complete pages off-screen, fast.

Node trees can be created without attaching them to a document. Then the whole tree can be attached with one call,

There’s no need to attach nodes individually. I don’t know why anyone would want to do that.


> I've used a technique called 'double buffering' to rerender complete pages off-screen, fast.

I'm absolutely not a web dev (as this comment will make clear), but I'm very surprised that browsers haven't been doing this all along. I just assumed that they did.


I'm sure they do, theres even a construct to do this called OffscreenCanvas


Any changes to the DOM aren't reflected on screen until your code stops running. If you see a partial update it's because your code is "await"ing something.


> then none of these libraries would exist.

I'd still have to use a framework for componentizing my components, scoping CSS, a namespaced store, etc


In all cases you're going to store and manage your state some place. What you describe is managing the state using the DOM itself, which would indeed be easier if the DOM wasn't directly tied to the screen, but which would still be very cumbersome because of the lack of appropriate APIs for managing the DOM as an application state.

If you were to go in this direction, you would eventually make the DOM less DOM-ish, and the painting mechanism more DOM-ish, effectively shifting everything one place and going full circle back to the frameworks.


Back in the VB6 days there was an option one had to toggle to enable the double buffer.

Without it all app paints had a noticeable lag.


We need DOM transactions.




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

Search: