Preventing double redrawing; updated tests.
@@ -410,4 +410,17 @@ jest.spyOn(vnode, "redraw");
h3.redraw(); expect(vnode.redraw).toHaveBeenCalled(); }); + + it("should not redraw while a other redraw is in progress", async () => { + const vnode = h3("div"); + await h3.init({ + routes: { + "/": () => vnode + }, + }); + jest.spyOn(vnode, "redraw"); + h3.redraw(true); + h3.redraw() + expect(vnode.redraw).toHaveBeenCalledTimes(1); + }); });
@@ -308,4 +308,28 @@ expect(v1).toEqual(v2);
v3.redraw({ node: n3, vnode: v4 }); expect(v3).toEqual(v4); }); + + it("should expose an $onrender special attribute to execute a callback after rendering", async () => { + let test; + const vnode = h3("div", { $onrender: () => (test = 1) }); + await h3.init({ + routes: { + "/": () => vnode, + }, + }); + expect(test).toEqual(1); + const vnode1 = h3("div", h3("div")); + const vnode2 = h3("div", h3("span", { $onrender: () => (test = 2) })); + const node1 = vnode1.render(); + vnode1.redraw({ node: node1, vnode: vnode2 }); + expect(test).toEqual(2); + const vnode3 = h3("div", h3("div", { $onrender: () => (test = 4) })); + const vnode4 = h3("div", [ + h3("div", { $onrender: () => (test = 4) }), + h3("div", { $onrender: () => (test = 5) }), + ]); + const node3 = vnode3.render(); + vnode3.redraw({ node: node3, vnode: vnode4 }); + expect(test).toEqual(5); + }); });
@@ -8001,7 +8001,7 @@ </ul>
</div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco – May 9, 2020</p> + <p><span class="copy"></span> Fabio Cevasco – May 16, 2020</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
@@ -70,6 +70,7 @@ this.data = {};
this.id = undefined; this.$key = undefined; this.$html = undefined; + this.$onrender = undefined; this.style = undefined; this.value = undefined; this.children = [];@@ -159,6 +160,7 @@ this.type = data.type;
this.id = data.id; this.$key = data.$key; this.$html = data.$html; + this.$onrender = data.$onrender; this.style = data.style; this.data = data.data; this.value = data.value;@@ -176,6 +178,7 @@ processProperties(attrs) {
this.id = this.id || attrs.id; this.$key = attrs.$key; this.$html = attrs.$html; + this.$onrender = attrs.$onrender; this.style = attrs.style; this.value = attrs.value; this.data = attrs.data || {};@@ -198,6 +201,7 @@ });
delete this.attributes.value; delete this.attributes.$key; delete this.attributes.$html; + delete this.attributes.$onrender; delete this.attributes.id; delete this.attributes.data; delete this.attributes.style;@@ -295,7 +299,9 @@ node.dataset[key] = this.data[key];
}); // Children this.children.forEach((c) => { - node.appendChild(c.render()); + const cnode = c.render(); + node.appendChild(cnode); + c.$onrender && c.$onrender(cnode); }); if (this.$html) { node.innerHTML = this.$html;@@ -315,7 +321,9 @@ (oldvnode.type === newvnode.type &&
oldvnode.type === "#text" && oldvnode !== newvnode) ) { - node.parentNode.replaceChild(newvnode.render(), node); + const renderedNode = newvnode.render(); + node.parentNode.replaceChild(renderedNode, node); + newvnode.$onrender && newvnode.$onrender(renderedNode); oldvnode.from(newvnode); return; }@@ -465,10 +473,10 @@ oldvnode.children.splice(notFoundInNew, 1);
} } else { // While there are children not found in oldvnode, add them and re-check - node.insertBefore( - newvnode.children[notFoundInOld].render(), - node.childNodes[notFoundInOld] - ); + const cnode = newvnode.children[notFoundInOld].render(); + node.insertBefore(cnode, node.childNodes[notFoundInOld]); + newvnode.children[notFoundInOld].$onrender && + newvnode.children[notFoundInOld].$onrender(cnode); oldvnode.children.splice( notFoundInOld, 0,@@ -596,16 +604,20 @@ }
if (!this.route) { throw new Error(`[Router] No route matches '${fragment}'`); } + redrawing = true; this.store.dispatch("$navigation", this.route); // Display View while (this.element.firstChild) { this.element.removeChild(this.element.firstChild); } const vnode = this.routes[this.route.def](); - this.element.appendChild(vnode.render()); + const node = vnode.render(); + this.element.appendChild(node); + vnode.$onrender && vnode.$onrender(node); this.setRedraw(vnode); window.scrollTo(0, 0); this.store.dispatch("$redraw"); + redrawing = false; }; processPath(); window.addEventListener("hashchange", processPath);@@ -627,6 +639,7 @@ };
let store = null; let router = null; +let redrawing = false; h3.init = (config) => { let { element, routes, modules, preStart, postStart, location } = config;@@ -705,13 +718,18 @@ }
return store.dispatch(event, data); }; -h3.redraw = () => { +h3.redraw = (setRedrawing) => { if (!router || !router.redraw) { throw new Error( "[h3.redraw] No application initialized, unable to update." ); } + if (redrawing) { + return; + } + redrawing = true; router.redraw(); + redrawing = setRedrawing || false; }; export default h3;
@@ -70,6 +70,7 @@ this.data = {};
this.id = undefined; this.$key = undefined; this.$html = undefined; + this.$onrender = undefined; this.style = undefined; this.value = undefined; this.children = [];@@ -159,6 +160,7 @@ this.type = data.type;
this.id = data.id; this.$key = data.$key; this.$html = data.$html; + this.$onrender = data.$onrender; this.style = data.style; this.data = data.data; this.value = data.value;@@ -176,6 +178,7 @@ processProperties(attrs) {
this.id = this.id || attrs.id; this.$key = attrs.$key; this.$html = attrs.$html; + this.$onrender = attrs.$onrender; this.style = attrs.style; this.value = attrs.value; this.data = attrs.data || {};@@ -198,6 +201,7 @@ });
delete this.attributes.value; delete this.attributes.$key; delete this.attributes.$html; + delete this.attributes.$onrender; delete this.attributes.id; delete this.attributes.data; delete this.attributes.style;@@ -295,7 +299,9 @@ node.dataset[key] = this.data[key];
}); // Children this.children.forEach((c) => { - node.appendChild(c.render()); + const cnode = c.render(); + node.appendChild(cnode); + c.$onrender && c.$onrender(cnode); }); if (this.$html) { node.innerHTML = this.$html;@@ -315,7 +321,9 @@ (oldvnode.type === newvnode.type &&
oldvnode.type === "#text" && oldvnode !== newvnode) ) { - node.parentNode.replaceChild(newvnode.render(), node); + const renderedNode = newvnode.render(); + node.parentNode.replaceChild(renderedNode, node); + newvnode.$onrender && newvnode.$onrender(renderedNode); oldvnode.from(newvnode); return; }@@ -465,10 +473,10 @@ oldvnode.children.splice(notFoundInNew, 1);
} } else { // While there are children not found in oldvnode, add them and re-check - node.insertBefore( - newvnode.children[notFoundInOld].render(), - node.childNodes[notFoundInOld] - ); + const cnode = newvnode.children[notFoundInOld].render(); + node.insertBefore(cnode, node.childNodes[notFoundInOld]); + newvnode.children[notFoundInOld].$onrender && + newvnode.children[notFoundInOld].$onrender(cnode); oldvnode.children.splice( notFoundInOld, 0,@@ -596,16 +604,20 @@ }
if (!this.route) { throw new Error(`[Router] No route matches '${fragment}'`); } + redrawing = true; this.store.dispatch("$navigation", this.route); // Display View while (this.element.firstChild) { this.element.removeChild(this.element.firstChild); } const vnode = this.routes[this.route.def](); - this.element.appendChild(vnode.render()); + const node = vnode.render(); + this.element.appendChild(node); + vnode.$onrender && vnode.$onrender(node); this.setRedraw(vnode); window.scrollTo(0, 0); this.store.dispatch("$redraw"); + redrawing = false; }; processPath(); window.addEventListener("hashchange", processPath);@@ -627,6 +639,7 @@ };
let store = null; let router = null; +let redrawing = false; h3.init = (config) => { let { element, routes, modules, preStart, postStart, location } = config;@@ -705,13 +718,18 @@ }
return store.dispatch(event, data); }; -h3.redraw = () => { +h3.redraw = (setRedrawing) => { if (!router || !router.redraw) { throw new Error( "[h3.redraw] No application initialized, unable to update." ); } + if (redrawing) { + return; + } + redrawing = true; router.redraw(); + redrawing = setRedrawing || false; }; export default h3;
@@ -70,6 +70,7 @@ this.data = {};
this.id = undefined; this.$key = undefined; this.$html = undefined; + this.$onrender = undefined; this.style = undefined; this.value = undefined; this.children = [];@@ -159,6 +160,7 @@ this.type = data.type;
this.id = data.id; this.$key = data.$key; this.$html = data.$html; + this.$onrender = data.$onrender; this.style = data.style; this.data = data.data; this.value = data.value;@@ -176,6 +178,7 @@ processProperties(attrs) {
this.id = this.id || attrs.id; this.$key = attrs.$key; this.$html = attrs.$html; + this.$onrender = attrs.$onrender; this.style = attrs.style; this.value = attrs.value; this.data = attrs.data || {};@@ -198,6 +201,7 @@ });
delete this.attributes.value; delete this.attributes.$key; delete this.attributes.$html; + delete this.attributes.$onrender; delete this.attributes.id; delete this.attributes.data; delete this.attributes.style;@@ -295,7 +299,9 @@ node.dataset[key] = this.data[key];
}); // Children this.children.forEach((c) => { - node.appendChild(c.render()); + const cnode = c.render(); + node.appendChild(cnode); + c.$onrender && c.$onrender(cnode); }); if (this.$html) { node.innerHTML = this.$html;@@ -315,7 +321,9 @@ (oldvnode.type === newvnode.type &&
oldvnode.type === "#text" && oldvnode !== newvnode) ) { - node.parentNode.replaceChild(newvnode.render(), node); + const renderedNode = newvnode.render(); + node.parentNode.replaceChild(renderedNode, node); + newvnode.$onrender && newvnode.$onrender(renderedNode); oldvnode.from(newvnode); return; }@@ -465,10 +473,10 @@ oldvnode.children.splice(notFoundInNew, 1);
} } else { // While there are children not found in oldvnode, add them and re-check - node.insertBefore( - newvnode.children[notFoundInOld].render(), - node.childNodes[notFoundInOld] - ); + const cnode = newvnode.children[notFoundInOld].render(); + node.insertBefore(cnode, node.childNodes[notFoundInOld]); + newvnode.children[notFoundInOld].$onrender && + newvnode.children[notFoundInOld].$onrender(cnode); oldvnode.children.splice( notFoundInOld, 0,@@ -596,16 +604,20 @@ }
if (!this.route) { throw new Error(`[Router] No route matches '${fragment}'`); } + redrawing = true; this.store.dispatch("$navigation", this.route); // Display View while (this.element.firstChild) { this.element.removeChild(this.element.firstChild); } const vnode = this.routes[this.route.def](); - this.element.appendChild(vnode.render()); + const node = vnode.render(); + this.element.appendChild(node); + vnode.$onrender && vnode.$onrender(node); this.setRedraw(vnode); window.scrollTo(0, 0); this.store.dispatch("$redraw"); + redrawing = false; }; processPath(); window.addEventListener("hashchange", processPath);@@ -627,6 +639,7 @@ };
let store = null; let router = null; +let redrawing = false; h3.init = (config) => { let { element, routes, modules, preStart, postStart, location } = config;@@ -705,13 +718,18 @@ }
return store.dispatch(event, data); }; -h3.redraw = () => { +h3.redraw = (setRedrawing) => { if (!router || !router.redraw) { throw new Error( "[h3.redraw] No application initialized, unable to update." ); } + if (redrawing) { + return; + } + redrawing = true; router.redraw(); + redrawing = setRedrawing || false; }; export default h3;