google / incremental-dom

An in-place DOM diffing library
http://google.github.io/incremental-dom/
Apache License 2.0
3.54k stars 180 forks source link

Document how keys work with existing DOM elements #311

Closed thepian closed 7 years ago

thepian commented 7 years ago

I was looking up how to add to a container with existing content, and there doesn't seem to be anything.

Scenario: You have a page rendered on the server with a long list of cards. You want the existing elements to be left alone and build/manipulate the tail. To avoid weird restrictions to layout the tail shouldn't have its own container element.

This seems to be straight forward. You just need to set keys on the initial cards and render additional cards with keys that don't clash. Correct?

Should getData be exposed in the API so you can access the keyMap on it? I suppose that accessing the __incrementalDOMData isn't the right way.

thepian commented 7 years ago

Relates to #275 Document importNode

sparhami commented 7 years ago

Just to clarify, even with keys, you still need to "rerender" the items at the head of the list. Incremental DOM uses the "key" attribute for any imported DOM as the element's key (so that if you re-order them or add a new item at the head, they get reused correctly). So lets say you rendered the following server side:

<ul>
  <li key="first">first</li>
  <li key="second">second</li>
</ul>

to add items to the end, you might do something like:

var items = ['first', 'second']; // items matching what was rendered server side
var container = ...;
...
items.push('third'); // add a new item

patch(container, function() {
  items.forEach(item => {
    elementOpen('li', item);
      text(item)
    elementClose('li');
  });
});
thepian commented 7 years ago

That's a shame. It means i have to rebuild a render function for what is there to begin with. It could be hundreds of elements. Ok, thanks for the clarification

sparhami commented 7 years ago

While I probably wouldn't recommend it in general, you can skip over nodes. You could do something like the following, which would let you skip over the server rendered items each time you patched.

// On initial boot up, keep track of what the last child was
const lastServerRenderedChild = container.lastChild;

function skipUntil(node) {
  while(currentPointer() && currentPointer() !== node) {
    skipNode();
  }
}

patch(container, () => {
  skipUntil(lastServerRenderedChild);
  items.forEach(item => ...);
});
thepian commented 7 years ago

Excellent solution, thanks