Feb 2022

React Flavored JavaScript Components

Picture yourself in prehistoric times, lurking in a den of DOM mutations and jQuery. State is hidden in innocuous looking input fields. event's are left loose to propogate. How can we take the hardened lessons of modern web frameworks and apply them to a codebase which eschews any sense of order?

Of the first order is clarifying the utility of modern frameworks

  • Components which encapsulate fragments of HTML for reuse. Those fragments can include behavior, as well as styles scoped to a component.
  • These components are stateful. State is not declared in the ether, but safely ensconced within a component. A checkbox has enough self awareness to answer the question, "Am I checked?", without excavating the DOM.
  • State is transparent. console.log your state and know your DOM. A convenience brought to you by One Way Data Binding. The DOM can bubble up events but is powerless over mutating state. That privilege belongs to your component.
  • Components can be customized by ingesting variables. Parent components can react to children by passing in callbacks.

...by way of example.

☝️ is a component built with modern JS.

Component interface

Stackblitz

PaginateBar = new PaginateBarComponent({
  selector: '#paginate-bar',
  onChange: (newPageNum) => (// API call or whatever),
  children: `
    <div style="color: Tomato; font-style: italic;">
      MY DASHBOARD
    </div>
  `,
});
 
PaginateBar.setValues({
  pageNum: 3,
  perPage: 50,
  totalRecords: 377,
  totalPages: 8,
});

Notice how we are mimicking React

Component definition

Stackblitz

class PaginateBarComponent {
  #selector;
  #onChange;
  #children;
  #pageNum;
  #perPage;
  #totalRecords;
  #totalPages;
 
  constructor({ selector, onChange, children }) {
    // constructor props
    this.#selector = selector;
    this.#onChange = onChange;
    this.#children = children;
 
    // setValues() props
    this.#pageNum = null;
    this.#perPage = null;
    this.#totalRecords = null;
    this.#totalPages = null;
 
    Object.preventExtensions(this);
 
    this.#setInitialDOM();
  }
 
  setValues({ pageNum, perPage, totalRecords, totalPages }) { ... }
 
  isLoading(value) { ... }
 
  #setInitialDOM() { ... }
 
  #updateDOM() { ... }
}

We are leveraging modern-ish JS

  • This approach obviates the need for a build step.

  • Modern frameworks provide guardrails to keep you on the beaten path. JavaScript is the wild west in comparison and requires much more discipline.

  • Whereas React effectively tracks prop changes and triggers rerenders, we must explicitly notify our component of prop changes via setValues.

  • Traditionally, we would render HTML upfront and mutate on demand. React allows you to declare your UI, and manages DOM updates for you (docs). We could emulate this approach by tearing down the DOM on prop/state change, but why would we?

If you have a hight tolerance for discomfort, jQuery + modern JS might be a mediocre subsitute for React-like frameworks. You will lose the awsome DX (Developer Experience) and only have yourself to blame 😉.

Frontend Masters has an article on vanilla JS apps which is worth checking out.

Take the code for a spin 👇