Remix.run Logo
efortis a year 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 a year ago | parent [-]

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

WickyNilliams a year 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 a year ago | parent [-]

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

WickyNilliams a year 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