WebReflection / lighterhtml

The hyperHTML strength & experience without its complexity πŸŽ‰
https://medium.com/@WebReflection/lit-html-vs-hyperhtml-vs-lighterhtml-c084abfe1285
ISC License
735 stars 20 forks source link

dynamic slots always re-render #26

Closed joshgillies closed 5 years ago

joshgillies commented 5 years ago

With the below code example the <span> will always be recreated during a render cycle:

import {render, html} from '//unpkg.com/lighterhtml?module';
todo(document.body);
function todo(node, items = []) {
  render(
    node,
    () => html`
      <ul>
        ${
          items.map(
            (what, i) => html`
              <li data-i="${i}" onclick="${remove}">${what}</li>
            `
          )
        }
      </ul>
      ${
        items.length > 1
          ? html`
              <span>There are ${items.length} items</span>
            `
          : null
      } <button onclick="${add}">add</button>
    `
  );
  function add() {
    items.push(prompt("do"));
    todo(node, items);
  }
  function remove(e) {
    items.splice(e.currentTarget.dataset.i, 1);
    todo(node, items);
  }
}

https://codepen.io/anon/pen/aMpexp?editors=0010

Attached also is a screenshot of the point at which the current and next dynamic slot is live in the document:

screen shot 2019-03-07 at 2 39 54 pm

joshgillies commented 5 years ago

Sorry I should have scanned the closed issues before raising this, and as such this is potentially a duplicate of #12?

@WebReflection when you have a moment would appreciate your input here. πŸ‘

WebReflection commented 5 years ago

It's normal, I can't know upfront how many nodes you are going to render, and the easyness of lighterhtml is all based on incremental renders. Regardless, what's the issue, exactly?

joshgillies commented 5 years ago

I can't know upfront how many nodes you are going to render

Understandable, and of course I can always use html.for(...) to explicitly tag the dynamic slot(s).

Regardless, what's the issue, exactly?

The issue here I feel is around consistency, in the above example, all nodes are preserved between renders except the one describing the <span>. My ideal here is that the <span> is preserved between renders, and only the dynamic portion of its innerText (There are ${items.length} items) is updated.

Maybe I'm misunderstanding how the lighterhtml.render function manages keyed components?


Edit: for additional context, I'm encountering this while working on a UI with a non-trivial conditionally rendered subtrees all of which are being thrown away with each subsequent render.

WebReflection commented 5 years ago

Keyed, in lighterhtml, means, same nodes, same references next time.

If you don't explicit references, the key is the index at which a node occurred.

next time, if the list of LIs shrinks, the index 1 would be for span, but who knows if it's the same span that before was in index 2 ... if the list grows, at index 2 there won't be a SPAN, but an LI, the span will be created at new index.

The template parsing is unique per scope, hence there's no slowdown there, and the node might be recreated, but you shouldn't care.

In general, indeed, nobody should care about having or not the same node, unless you have performance issue, but trust me it's super hard to even have that πŸ‘‹

WebReflection commented 5 years ago

P.S. as it is explicit for the benchmark, lighterhtml is strictly keyed only if you use html.for(...) otherwise is hybrid keyed as explained in my previous reply