Supporting the key-prop

This commit is contained in:
Miel Truyen 2019-11-09 00:50:32 +01:00
parent 51f894c616
commit 5704b72542
4 changed files with 49 additions and 21 deletions

View File

@ -26,17 +26,6 @@ export function getNodeMeta(vnode){
* @property {Element} [parent] - The parent element (TODO not sure what this will do when specified; Insert it as child element of the parent where?) * @property {Element} [parent] - The parent element (TODO not sure what this will do when specified; Insert it as child element of the parent where?)
*/ */
/**
* Temporary data structure for listing an old VNode
* @typedef VOldQueueItem
* @interface
* @category VDOM.renderer
* @property {VNode} vnode - The old vnode
* @property {VRenderQueueItemMetadata} meta - Meta data for the item such as normedType and the renderer to use(from a preprocessing stage)
* @property {Element} element - The matching element
**/
/** /**
* This exists as a very basic example/test for JSX-to-DOM * This exists as a very basic example/test for JSX-to-DOM
* @category VDOM * @category VDOM
@ -103,7 +92,7 @@ export function render(vnode, opts = {}) {
// Only items with a renderer are tracked (any other are undefined or null and shoulnd't be rendered at all) // Only items with a renderer are tracked (any other are undefined or null and shoulnd't be rendered at all)
let childType = meta.normedType; let childType = meta.normedType;
if(!meta.renderer.remove) childType = 'node'; // Treat anything that doesnt have a special remove-function as ChildNode-type (e.g. it shows up in Element.childNodes) if(!meta.renderer.remove) childType = 'node'; // Treat anything that doesnt have a special remove-function as ChildNode-type (e.g. it shows up in Element.childNodes)
childTypes.add(childType);// Tract that children of this type exist and should be iterated later childTypes.add(childType);// Track that children of this type exist and should be iterated later
vChildren[childType] = vChildren[childType] || []; // Make sure the array exists vChildren[childType] = vChildren[childType] || []; // Make sure the array exists
vChildren[childType].push({ vChildren[childType].push({
item: { item: {
@ -142,13 +131,17 @@ export function render(vnode, opts = {}) {
curElement = curElement.nextSibling; curElement = curElement.nextSibling;
} }
} }
childTypes.add(childType);// Tract that children of this type exist and should be iterated later childTypes.add(childType);// Track that children of this type exist and should be iterated later
oldVChildren[childType] = oldVChildren[childType] || []; // Make sure the array exists oldVChildren[childType] = oldVChildren[childType] || []; // Make sure the array exists
oldVChildren[childType].push({ let oldItem = {
vnode: next, vnode: next,
element: childElement, element: childElement,
meta: meta meta: meta
}); };
oldVChildren[childType].push(oldItem);
if(next.props?.key){
state.keyedElements.set(next.key,oldItem);
}
} }
} }
} }
@ -156,17 +149,42 @@ export function render(vnode, opts = {}) {
let sortedChildTypes = Array.from(childTypes).sort((a,b)=>a==='node'?1:-1); // Always do ChildNode-types last let sortedChildTypes = Array.from(childTypes).sort((a,b)=>a==='node'?1:-1); // Always do ChildNode-types last
let queuedItems = []; let queuedItems = [];
let previous = null; /**@type {VRenderQueueItem}*/ let previous = null;
for(let childType of sortedChildTypes){ for(let childType of sortedChildTypes){
let newChildren = vChildren[childType]; let newChildren = vChildren[childType];
let oldChildren = oldVChildren[childType]; let oldChildren = oldVChildren[childType];
while(newChildren && newChildren.length){ while(newChildren && newChildren.length){
let child = newChildren.splice(0,1)[0]; let child = newChildren.splice(0,1)[0];
let oldChild = oldChildren && oldChildren.splice(0,1)[0];
// Key handling
let childKey = child.item.vnode.props?.key;
/**@type {VOldQueueItem}*/ let oldChild;
if(childKey){
oldChild = state.keyedElements.get(childKey);
if(oldChild) {
if (oldChildren && oldChildren[ 0 ] === oldChild) {
// Old keyed child already in the right place (just clear it from the queue);
oldChildren.splice(0, 1);
} else {
// Old keyed child not already in the right place
let indexOfKeyed = oldChildren.indexOf(oldChild);
if(indexOfKeyed) {
oldChildren.splice(indexOfKeyed, 1);
item.host.removeChild(oldChild.element);
}
if (previous) {
previous.item.host.after(oldChild.element);
} else {
item.parent.prepend(oldChild.element);
}
}
}
}
if(!oldChild) oldChild = oldChildren && oldChildren.splice(0,1)[0];
child.previous = previous; child.previous = previous;
if(oldChild && child.meta.normedType === oldChild.meta.normedType){ if(oldChild && child.meta.normedType === oldChild.meta.normedType && childKey===oldChild.vnode.props?.key){
// Update old-child // Update old-child
child.item.host = oldChild.element; child.item.host = oldChild.element;
child.item.old = oldChild.vnode; child.item.old = oldChild.vnode;

View File

@ -24,6 +24,15 @@ import "./vnode";
* @property {VRenderItem} previous - The item that will have been inserted before this one * @property {VRenderItem} previous - The item that will have been inserted before this one
**/ **/
/**
* Temporary data structure for listing an old VNode
* @typedef VOldQueueItem
* @interface
* @category VDOM.renderer
* @property {VNode} vnode - The old vnode
* @property {VRenderQueueItemMetadata} meta - Meta data for the item such as normedType and the renderer to use(from a preprocessing stage)
* @property {Element} element - The matching element
**/
/** /**
* Global rendering-state when rendering a tree of VNodes * Global rendering-state when rendering a tree of VNodes
@ -32,5 +41,5 @@ import "./vnode";
* @category VDOM.renderer * @category VDOM.renderer
* @property {Array.<VRenderQueueItem>} queue - The queue of items to be rendered * @property {Array.<VRenderQueueItem>} queue - The queue of items to be rendered
* @property {Array.<[Function,Element]>} refs - Ref-callback functions be called when rendering is done * @property {Array.<[Function,Element]>} refs - Ref-callback functions be called when rendering is done
* @property {Map.<string, VNode>} keyedElements - A map of keyed elements (TODO this needs refining) * @property {Map.<string, VOldQueueItem>} keyedElements - A map of (old) keyed elements
**/ **/

View File

@ -25,6 +25,7 @@ export class MyTodo extends CustomElement{
> >
{this.todos.map(item => {this.todos.map(item =>
<todo-item <todo-item
key={item.id}
model={ item.id } model={ item.id }
checked={ item.checked } checked={ item.checked }
> >