-
Notifications
You must be signed in to change notification settings - Fork 12
Closed
Labels
0.19.0JSXdocumentationImprovements or additions to documentationImprovements or additions to documentationexpirementfeatureNew feature or requestNew feature or request
Milestone
Description
Type of Change
Feature
Summary
Building off the outcome of #87 , would like to, and have already started playing around with, a more "fine grained" observability model; one that doesn't require an entire re-render and blowing out innerHTML, but can instead more acutely update DOM nodes (textContent, setAttribute) instead.
Details
So for example, taking our Counter component
export default class Counter extends HTMLElement {
...
render() {
const { count } = this;
return (
<div>
<button onclick={this.count -= 1}> -</button>
<span>You have clicked <span class="red">{count}</span> times</span>
<button onclick={this.count += 1}> +</button>
</div>
);
}
}Which would produce this compiled output
export default class Counter extends HTMLElement {
static get observedAttributes() {
return ['count'];
}
attributeChangedCallback(name, oldValue, newValue) {
function getValue(value) {
return value.charAt(0) === '{' || value.charAt(0) === '[' ? JSON.parse(value) : !isNaN(value) ? parseInt(value, 10) : value === 'true' || value === 'false' ? value === 'true' ? true : false : value;
}
if (newValue !== oldValue) {
switch (name) {
case 'count':
this.count = getValue(newValue);
break;
}
this.render();
}
}
...
}Instead, we would want the compiled output to look something like this instead
export default class Counter extends HTMLElement {
static get observedAttributes() {
return ['count'];
}
attributeChangedCallback(name, oldValue, newValue) {
function getValue(value) {
return value.charAt(0) === '{' || value.charAt(0) === '[' ? JSON.parse(value) : !isNaN(value) ? parseInt(value, 10) : value === 'true' || value === 'false' ? value === 'true' ? true : false : value;
}
if (newValue !== oldValue) {
switch (name) {
case 'count':
this.count = getValue(newValue);
break;
}
this.update(name, oldValue, newValue);
}
}
update(name, oldValue, newValue) {
const attr = \`data-wcc-\${name}\`;
const selector = \`[\${attr}]\`;
this.querySelectorAll(selector).forEach((el) => {
const needle = oldValue || el.getAttribute(attr);
switch(el.getAttribute('data-wcc-ins')) {
case 'text':
el.textContent = el.textContent.replace(needle, newValue);
break;
case 'attr':
if (el.hasAttribute(el.getAttribute(attr))) {
el.setAttribute(el.getAttribute(attr), newValue);
}
break;
}
})
}
...
}Additional Thoughts:
- It might stand to reason we should map updates back to attributes, to keep things in sync? But not sure if this co-mingling is good or bad? Probably if state is meant to go "out" it should be done through custom events instead? Will need to play around with this a bit
- Would be good to explore tagged template functions as part of this, if not for at least the underlying templating mechanics (as opposed to instead of using JSX directly)
- Although we only scan the
renderfunction forthisreferences, would we do ourselves a service by scanningconstructortoo, maybe for init values and / or something related to SSR? - Not sure if
datasetcould be useful for anything?
Metadata
Metadata
Assignees
Labels
0.19.0JSXdocumentationImprovements or additions to documentationImprovements or additions to documentationexpirementfeatureNew feature or requestNew feature or request
Type
Projects
Status
✅ Done
Status
✅ Done