WebReflection / uland

A µhtml take at neverland
ISC License
108 stars 2 forks source link

Uncaught unobservable when uland component returns >1 null holes with an effect #18

Closed bcomnes closed 1 year ago

bcomnes commented 1 year ago

Ran into a weird issue, perhaps user error.

If you try to render a uland component that returns >1 null holes only (so basically an empty render body), and the component includes an effect, uland throws an uncaught observable error.

Here is a contrived example:

import {Component, render, html, useState, useEffect} from 'https://unpkg.com/uland?module'

const emptyComponent = Component(() => {
  // Toggle to true to return at least one non-null hole. Issue goes away
  const toggle = false

   useEffect(() => {
     console.log('emptyComponentMounted')
     return () => {
       console.log('emptyComponentUnmounted')
     }
  }, [])

  // Returning a single null does not have the issue. Must return >1 nulls
  return html`
    ${toggle ? html`<div>foo</div>` : null}
    ${toggle ? html`<div>foo</div>` : null}
  `
})

render(document.querySelector('.app'), () => html`${emptyComponent()}`)
Screen Shot 2022-10-06 at 9 42 55 AM

Here is a quick codepen: https://codepen.io/bcomnes-the-selector/pen/yLjxgpp?editors=1011

I would expect the component to just render to nothing but the effect to still run, instead of throw. Returning an empty div fixes the issue, or wrapping the body with a div also works. It would be nice if this works though. Let me know if I can provide more info or help out in any way.

WebReflection commented 1 year ago

Would filtering empty nodes work? Diffing assumes nodes in the array, not null / void values

WebReflection commented 1 year ago

I’ve checked the code … this is a well known uhtml gotcha … you can have an empty html tag instead or use an array concat to simulate zero to many diffs

bcomnes commented 1 year ago

NP. Its sort of a weird edge case. Do you have a small example of the array concat technique?

WebReflection commented 1 year ago

Something like:

${[].concat(toggle ? html`<div>foo</div>` : [])}
bcomnes commented 1 year ago

Clever. Thank you! I will just do this.