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 upevents
but is powerless over mutatingstate
. 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
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
- The component attaches itself to a selector.
constructor
andsetValues
ingestprops
.- Consumers can provide an
onChange
callback. - Arbitrary HTML can be included via
children
.
Component definition
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
- Classes for encapsulation.
- Private fields and methods provide a measure of safety.
- Object.preventExtensions disables new fields.
- Template tags for defining chunks of HTML. It's the closest you'll get to JSX.
-
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 ofprop
changes viasetValues
. -
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 👇