List Rendering
The Duck way of list rendering.
Demo
live demo | files
Recipe
function Store(data) {
  const events = {};

  let list = data.list || [];
  indexList(list);

  function getList() { return list; }
  function setList(values) {
    if(values) { list = values; }
    indexList(list);
    emit('list');
  }
  function pushToList(item) {
    list.push(item);
    setList();
  }
  function removeFromList(item) {
    item.remove = true;
    setList();
  }

  return {
    getList,
    setList,
    pushToList,
    removeFromList,
    on, off
  };

  // ...

  ////////////////////////////////
  // list rendering
  function indexList(list) {
    for(let i = 0; i < list.length; i++) {
      // remove items marked for removal
      if(list[i].remove) {
        list.splice(i, 1)[0].removed = true;
        i--;
        continue;
      }
      // apply index
      list[i].i = i;
    }
  }
  ////////////////////////////////
}
JavaScript
function Component(data) {
  let elem;

  function construct() {
    elem = document.createElement('div');
    update();
  }

  function update() {
    elem.innerHTML = data.text;
  }

  // ...
}
JavaScript
function App(data) {
  let elem;
  const store = data.store;

  let list = [];

  function construct() {
    elem = document.createElement('div');
    update();
  }

  function update() {
    // params ~ storeList, list, listElem, componentCreate, componentUpdate
    updateList(store.getList(), list, elem,
      () => { return new Component({}); },
      (component, ref) => {
        if(component.data.text != ref.value) {
          component.data.text = ref.value;
          component.update();
        }
      },
    );
  }

  // ...

  ////////////////////////////////
  // list rendering
  function updateList(storeList, list, listElem, componentCreate, componentUpdate) {
    const slots = new Array(storeList.length);
    // flush list into slots, remove items marked as removed
    while(list.length) {
      const item = list.shift();
      if(item.data.ref.removed) {
        listElem.removeChild(item.elem);
        item.destroy();
        continue;
      }
      slots[item.data.ref.i] = item;
    }
    // componentCreate for empty slots, repopulate list, mount unmounted items
    for(let i = 0; i < slots.length; i++) {
      if(!slots[i]) {
        const ref = storeList[i];
        slots[i] = componentCreate(ref);
        slots[i].data.ref = ref;
      }
      const component = list[i] = slots[i];
      if(!component.data.mounted) {
        listElem.insertBefore(component.elem, listElem.childNodes[i+1]);
        component.enter();
        component.data.mounted = true;
      }
    }
    // sort elements, call componentUpdate
    for(let i = 0; i < list.length; i++) {
      const component = list[i];
      if(listElem.childNodes[i] != component.elem) {
        listElem.insertBefore(component.elem, listElem.childNodes[i+1]);
      }
      componentUpdate(component, component.data.ref);
    }
  }
  ////////////////////////////////
}
JavaScript
In Practice
Be sure to indexList() before the list is rendered.
It's useful to have a setList() function that just does indexList() and emit() when no parameters are passed. This is useful when performing list operations and then indexing/emitting afterward.
To remove a list item, you can just set item.remove = true. The next indexList() call will splice the item from the list and set item.removed = true for you.
Write as many store setter functions as you want to do things like add/remove/move list items. Whatever functionality make sense for your application.
Call update() at the end of construct() and then call updateList() in update() in the component that contains the list.
Subscribe to the store event for the list in the component that contains the list.
Walkthrough
In Duck, stores contain "state" and components do "whatever" in order to display that state. In the case of list rendering, the "state" is a list and the "whatever" is to maintain DOM elements to represent/display that particular list.
Cool! So... Where do we start?
Well, let's say, in Duck, each item in a list should be able to support multiple fields, for example, a single item may have the follwing properties, { title, description, price }. Next, let's make a rule for ourselves that, in Duck, for each item in a list, there should be a corresponding DOM element that needs to reference that item and "represent" it visually.
Sounds reasonable, right?
So, list items need to be able to contain multiple properties and need to be able to be referenced. What is something simple in JavaScript that has these properties? Hmmm... A plain object!
This leads us to the first caveat of list rendering in Duck,
CAVEAT: All Duck list items must be objects.
Now, let's say that I'm a component in Duck trying to render a list, so I call store.getList(), but how do I actually render it?
Well, I could simply iterate through the list, create a DOM element representing each item, and attach them to the DOM one at a time. But, what if items get added/removed to/from the list? What if items get moved to different indexes within the list? How would you know which DOM element is representing which list item at that point? How would you know which DOM elements to add/remove without destroying the whole DOM and rebuilding it?
Alright, alright... Obviously that simple list rendering algorithm probably isn't ideal. So, we need something that can both manage a DOM element and also contain other data such as a reference to an item within a list in a store. What is something in Duck that has these properties? Hmmm... A Component!
We can use components to hold a references to unique items in a list, and when list items are updated, the corresponding component that is referencing that item can be updated! And, instead of iterating through the list from store.getList(), we instead iterate through components themselves and check if the corresponding referenced item for that component has been updated.
This leads us to the next caveat of list rendering in Duck,
CAVEAT: All Duck list items are represented by Components.
Sounds great, but there are a few gotchas.
If an item is moved within a list, the component wouldn't have any way of knowing the index of the referenced item within the list.
If an item is removed from a list, the component wouldn't have any way of knowing that the referenced item was removed.
To solve the item index gotcha, we "index the items" by adding a property into each list item that tells the item where it is within the list.
This leads us to the next caveat of list rendering in Duck,
CAVEAT: You must always indexList() a list before it is rendered. This includes when the list is created and right before emit() calls.
To solve the removal gotcha, when removing an item from a list, we simply add a property to the item to flag that it has been removed.
That leads us to the final caveat of list rendering in Duck,
CAVEAT: When removing an item from a list, you must also set item.removed = true so that subscribers can know to remove the corresponding DOM element.
And, that is the Duck way of list rendering!
The recipe maintains a local list of components and ensures that it matches the store list. When the store emits that a list has been updated, the recipe goes through the local list and removes components that are marked as removed. Then, the recipe creates components that don't exist yet in the local list but exist in the store list. Lastly, the recipe sorts the DOM elements to make sure they are in the same order as the corresponding store items.
Notes
splice() is your swiss army knife for list operations
// remove items
list.splice(index, count).forEach((item) => { item.removed = true; });

// add items
list.splice(index, 0, { value: 1 }, { value: 2 });

// move item
list.splice(toIndex, 0, list.splice(fromIndex, 1)[0]);
JavaScript
Of course, other array functions are great as well, like pop() and push(). I simply find splice() to be extremely versatile.