Given the following:
import { createProjector, h, VNode } from 'maquette/maquette';
const node = h('dojo-panel-tabbed', {
id: 'tabbed-panel'
}, [
h('ul', {}, [
h('li', { classes: { active: false }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ]),
h('li', { classes: { active: true }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ]),
h('li', { classes: { active: false }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ]),
h('li', { classes: { active: false }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ])
])
]);
function render(): VNode {
return node;
}
const projector = createProjector({});
projector.append(document.body, render);
const next = document.getElementById('next');
next.addEventListener('click', (event) => {
console.log('before', node.children[0].children[1].properties.classes, node.children[0].children[2].properties.classes);
node.children[0].children[1].properties.classes['active'] = false;
node.children[0].children[2].properties.classes['active'] = true;
console.log('after', node.children[0].children[1].properties.classes, node.children[0].children[2].properties.classes);
projector.scheduleRender();
});
Maquette does not appear to notice changes to changes to already rendered VNodes. The classes do not change if a VNode is updated. I maybe missing something, but is it not possible to reuse VNodes?
To explain a bit, where I ran into an issue trying to create a tabbed component, where the VNodes for the tab bar were always regenerated, but the tab's contents would only be calculated for the tab coming into view, but then updating the classes of any tab contents already rendered. That way, if the tab were to come back into view, but its content had not changed, it would be as simple as adding a class back.
I have noticed though if I dropped a VNode out of one render and then return it later, the VNode will be full parsed.
I even tried to "reuse" just VNodeProperties, but that also appeared to exhibit the same behaviour, that if it was present from one render to the next, changes to its properties were not deeply noticed.
I can get it to work, only if I do something like this:
import { createProjector, h, VNode } from 'maquette/maquette';
let clicked = false;
function render(): VNode {
return h('dojo-panel-tabbed', {
id: 'tabbed-panel'
}, [
h('ul', {}, [
h('li', { classes: { active: false }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ]),
h('li', { classes: { active: !clicked }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ]),
h('li', { classes: { active: clicked }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ]),
h('li', { classes: { active: false }, key: {} }, [ h('div.tab-label', [ 'tab 1' ]), h('div.tab-close', [ 'X' ]) ])
])
]);
}
const projector = createProjector({});
projector.append(document.body, render);
const next = document.getElementById('next');
next.addEventListener('click', (event) => {
clicked = true;
projector.scheduleRender();
});
In my opinion, this is rather surprising behaviour and means that it is difficult to not fully recalculate the whole of the virtual DOM on every render, which doesn't properly represent the application state, as from render to render, only small changes are made to the VDOM.