Remix.run Logo
efortis 3 days ago

I use a helper similar to React.createElement.

  const state = { count: 0 }

  const init = () => document.body.replaceChildren(App())
  init()
  
  function App() {
    return (
      h('div', null,
        h('output', null,  `Counter: ${state.count}`),
        h(IncrementButton, { incrementBy: 2 })))
  }
  
  function IncrementButton({ incrementBy }) {
    return (
      h('button', {
        className: 'IncrementButton',
        onClick() { 
          state.count += incrementBy
          init()
        }
      }, 'Increment'))
  }
  

  function h(elem, props = null, ...children) {
    if (typeof elem === 'function')
      return elem(props)
  
    const node = document.createElement(elem)
    if (props)
      for (const [key, value] of Object.entries(props))
        if (key === 'ref')
          value.current = node
    else if (key.startsWith('on'))
      node.addEventListener(key.replace(/^on/, '').toLowerCase(), value)
    else if (key === 'style')
      Object.assign(node.style, value)
    else if (key in node)
      node[key] = value
    else
      node.setAttribute(key, value)
    node.append(...children.flat().filter(Boolean))
    return node
  }

Working example of a dashboard for a mock server: https://github.com/ericfortis/mockaton/blob/main/src/Dashboa...
simonw 3 days ago | parent [-]

That looks like it replaces the entire document every time state changes. How's the performance of that?

WickyNilliams 3 days ago | parent [-]

Even if performance is fine, the big usability issue is that it will blow away focus, cursor position etc every render. Gets very painful for keyboard use, and of course is a fatal accessibility flaw

efortis 3 days ago | parent [-]

yes, that’s the downside, focus is lost on init()

WickyNilliams 3 days ago | parent [-]

Really wish the browser gave us better APIs for updating the DOM. Creation is extremely easy, but after that you either have to invent some reconciliation system or stick with imperative updates