logoalt Hacker News

How to Build Reactive Declarative UI in Vanilla JavaScript

28 pointsby javatutstoday at 11:31 AM14 commentsview on HN

Comments

matharmintoday at 12:47 PM

There are a bunch of utilities that don't actually _do_ anything useful. The proxy in this example is used for nothing other than debug logs. The DOM utility layer just slightly reduces the number of LOC to create a DOM node.

And then you end up with consumer code that is not actually declarative? The final code still directly manipulates the DOM. And this shows the simplest possible example - creating and removing nodes. The difficult part that libraries/frameworks solve is _updating_ the DOM at scale.

my_throwaway23today at 1:46 PM

Have you heard of hyperapp? From the official [0][@hyperapp/html]:

    import { app } from "https://unpkg.com/hyperapp";
    import {
        main,
        h1,
        button,
        text,
    } from "https://unpkg.com/@hyperapp/html?module";

    const Subtract = (state) => ({ ...state, count: state.count - 1 });
    const Add = (state) => ({ ...state, count: state.count + 1 });

    const page = ({count}) =>
        main([
            h1(text(count)),
            button({ onclick: Subtract }, text("-")),
            button({ onclick: Add }, text("+")),
        ]);

    app({
        init: (count = 0) => ({ count }),
        view: page,
        node: document.getElementById("app"),
      })
I can't imagine building anything anymore with the overly verbose bloat that is React.

[0]: https://github.com/jorgebucaran/hyperapp/tree/main/packages/...

show 1 reply
efortistoday at 1:33 PM

I'm experimenting with recreating the whole DOM tree like this:

  function render() {
    restoreFocus(() => 
      document.body.replaceChildren(App()))
  }

  function App() {
    return (
      createElement('div', { className: 'App' }, 
        createElement('h1', null, 'Hello, World')))
  }

  function createElement(tag, props, ...children) {
    const elem = document.createElement(tag)
    for (const [k, v] of Object.entries(props || {}))
           if (k === 'ref')        v.elem = elem
      else if (k === 'style')      Object.assign(elem.style, v)
      else if (k.startsWith('on')) elem.addEventListener(k.slice(2).toLowerCase(), ...[v].flat())
      else if (k in elem)          elem[k] = v
      else                         elem.setAttribute(k, v)
    elem.append(...children.flat().filter(Boolean))
    return elem
  }

`restoreFocus` is here:

https://github.com/ericfortis/mockaton/blob/main/src/client/...

Results so far:

Rendering the whole DOM tree (instead of VDOMs) is a fast process. The slow part is attaching (committing) elements to the doc. For example, I have a test of 20,000 elements which takes <30ms to render, while attaching them takes 120ms.

Since the performance is mainly bound to the commit phase, with a DOM merging library, or hopefully, if we get a native API such as `document.replaceChildren(...App(), { merge: true })`, this approach could be better.

Caveats:

Although it restores focus, that's not the only thing we need to preserve, we also need to preserve scroll position and cursor position.

So to work around that, I still have to step out fully declarative, by just replacing the part that changed. For example, here I had to do manually mutate the DOM:

https://github.com/ericfortis/mockaton/blob/main/src/client/...

show 1 reply
xutopiatoday at 12:36 PM

For the life of me I don’t understand why people absolutely insist on using JavaScript to render HTML. Backend frameworks do HTmL just fine.

DOM manipulations can be simplified to just a few actions: remove, Add, change.

The other types of manipulations and interactive features can be sprinkles of JavaScript instead of hundreds of kilobytes of the stuff.

HTMX, Hotwire/Turbo, LiveView are just so much saner to me.

show 3 replies
dizlexictoday at 1:59 PM

I ask a LLM to do it :'(

vntoktoday at 12:54 PM

This is a really weird website, I glanced over a bunch of different articles and all read like AI slop to me.

Indeed, a detecting tool like GPT Zero is "highly confident" that 97% of this article is AI generated, while AI Detector returns "We are 100% confident that the text scanned is AI-generated".

Curious if this is an uncanny valley situation, because there aren't that many tells (dashes, etc.) in the text itself. Does it feel the same to you?

show 1 reply
draw_downtoday at 1:36 PM

[dead]