Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fine Grained Inferred Observability for JSX #108

Open
thescientist13 opened this issue Jan 4, 2023 · 0 comments · May be fixed by #110
Open

Fine Grained Inferred Observability for JSX #108

thescientist13 opened this issue Jan 4, 2023 · 0 comments · May be fixed by #110
Assignees
Labels
expirement feature New feature or request JSX
Milestone

Comments

@thescientist13
Copy link
Member

thescientist13 commented Jan 4, 2023

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 render function for this references, would we do ourselves a service by scanning constructor too, maybe for init values and / or something related to SSR?
  • Not sure if dataset could be useful for anything?
@thescientist13 thescientist13 added feature New feature or request expirement JSX labels Jan 4, 2023
@thescientist13 thescientist13 added this to the 1.0 milestone Jan 4, 2023
@thescientist13 thescientist13 self-assigned this Jan 4, 2023
@thescientist13 thescientist13 changed the title Coarse Grained (Inferred) Observability for JSX Fine Grained (Inferred) Observability for JSX Jan 4, 2023
@thescientist13 thescientist13 linked a pull request Jan 4, 2023 that will close this issue
7 tasks
@thescientist13 thescientist13 changed the title Fine Grained (Inferred) Observability for JSX Fine Grained Inferred Observability for JSX Jan 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
expirement feature New feature or request JSX
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant