Modern Front-end with Older Tech
Tags: javascript, webdev, react, frontend
Published: 2020-01-23 21:39:20

---
title: Modern Front-end with Older Tech
published: true
description: 
tags: JavaScript, webdev, react, Front-end,
cover_image: https://frontendmasters.com/books/front-end-handbook/2019/assets/images/web-tech-employed.jpg
---
If you just want to see the code:
1. Here's our finished project: https://codesandbox.io/s/component-example-9796w

2. Here's the project with my very lightweight library (note the fewer lines): https://codesandbox.io/s/domponent-example-ij1zs

Hey, so I'm a Senior Front-end Developer.  I've built performant web UI Components for enterprise-level apps and multi-national companies with AngularJS, React, Vue, jQuery Plugins, and Vanilla JS.
They all have their pluses.  Let's go over them quickly:

1. AngularJS, you can just augment your HTML and build full-fledged complex UIs.
2. jQuery Plugins, you can just add some classes and attributes to HTML and the plugins will do the rest.
3. React, the entire app is component based, easy to read, and trivial to reuse.
4. Vue, you can implement an AngularJS-type solution with an entirely component-driven approach.
5. Vanilla JS, you don't have pull in any libraries and you can choose whatever lightweight solution you want.

For each approach you can implement UI as a function of state.  For some (Vue and React) it's easier done with use of Virtual DOM (look it up if you need.  It's super cool).

However, what if you're stuck with older tech?  What if you're working with Razor or Pug or Thymeleaf?  And additionally, you're not using REST APIs?  You have some advantages (SSR by default, SEO-friendly), but you have a TON of drawbacks (lack of Virtual DOM, ergo difficult/verbose rerenders).

With classic front-end web development you lack simplified component state, component lifecycles, scoped models, granular control over model changes.  These are all complex to implement and a built-in part of React, Vue, Knockout, Angular, etc.

But with some build tools (webpack, parcel, rollup, grunt, gulp) and some incredibly battle-tested template languages (Thymeleaf, Pug, Razor) you can build UI Components with incredible ease.

Here's how I do it with my older tech stack:

## The directory structure
```
FrontEnd
|
|___components
         |
         |__MyComponent
         |        |
         |        |___MyComponent.pug/.html/.cshtml
         |        |___MyComponent.scss
         |        |___MyComponent.js
         |
         |__MyOtherComponent
                  |
                  |___MyOtherComponent.pug/.html/.cshtml
                  |___MyOtherComponent.scss
                  |___MyOtherComponent.js
```
Let's run through this.

In a React app, you would have 1 less file.  You might even have two less files.
You'd remove the `html` and possibly the `scss`.  You'd have your HTML as part of a `JSX` file.  You may even have CSS in JS.  So it might be a single file component.  This is similar to a `.vue` file.

We're just actually breaking it out here.  Screw 1 file, let's go classic and have 3.  Logic in JS, Structure in HTML, Look in SCSS. Now, each file:

### HTML
Let's make a simple Counter.  It's going to show the count and offer and increment and decrement option

```html
<div>
  <p>Your Count: 
   <span>0</span>
  </p>
  <button type="button">
   -
  </button>
  <button type="button">
   +
  </button>
</div>
```

Cool!  This is gonna look terrible and make people cry.  So let's write some styles.  

### SCSS
We will be using SCSS and BEM syntax. It will be imported into the .js file for the component. Let's boogie:
```scss
.Counter{
  padding: 1rem;

  &__count{
   font-size: 2.5rem;
  }

  &__btn{
   padding:.5rem;
   margin: .5rem;

   &--increment{
     background: lightgreen;
   }

   &--decrement{
     background: lightblue;
   }
  
  }
}
```

And let's update our HTML
```html
<div class="Counter">
  <p>Your Count: 
   <span class="Counter__count">0</span>
  </p>
  <button type="button" class="Counter__btn Counter__btn--decrement">
   -
  </button>
  <button type="button" class="Counter__btn Counter__btn--increment">
   +
  </button>
</div>
```

Hold up!  What's with the capitalized class name?
This is simply a preference of mine since it's standard practice in React apps to name your components Capitalized. But you can do whatever you want.

### JS
Ok, let's make this reusable JS with a default `count` of `0`.  We're going to do this poorly at first and then fix it up slowly.  So stay with me here :)

```js
import './Counter.scss'

class Counter {
  constructor() {
    this.count = 0;
    this.countEl = document.querySelector(".Counter__count");
    this.incBtn = document.querySelector(".Counter__btn--increment");
    this.decBtn = document.querySelector(".Counter__btn--decrement");

    this.incBtn.addEventListener("click", this.increment.bind(this));
    this.decBtn.addEventListener("click", this.decrement.bind(this));
  }

  increment() {
    ++this.count;
    this.updateDOM();
  }

  decrement() {
    --this.count;
    this.updateDOM();
  }

  updateDOM() {
    this.countEl.textContent = this.count;
  }
}

new Counter(); 

```

NOTE:  I'm using `bind` under the assumption you are not using Babel... yet
Read this: 
https://www.freecodecamp.org/news/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56/

Ok there are more than a few issues with this approach.  Let's focus on one:

_Using CSS classes (meant for styling only) to handle UI_

This is a big one.  Relying on CSS classes or even HTML element types to access DOM is a big boo boo.  If you change your class name or the element type you could be breaking functionality of your app!

So how do we address this?  There are a couple approaches:

1. JS-specific classes in your HTML
2. Using special `data-` attributes

We're going to use method #2:

```html
<div class="Counter">
  <p>Your Count: 
   <span class="Counter__count" data-count="true">0</span>
  </p>
  <button type="button" data-dec-btn="true" class="Counter__btn Counter__btn--decrement">
   -
  </button>
  <button type="button" data-inc-btn="true" class="Counter__btn Counter__btn--increment">
   +
  </button>
</div>
```

```js
import './Counter.scss'

class Counter {
  constructor() {
    this.count = 0;
    this.countEl = document.querySelector("[data-count]");
    this.incBtn = document.querySelector("[data-inc-btn]");
    this.decBtn = document.querySelector("[data-dec-btn]");

    this.incBtn.addEventListener("click", this.increment.bind(this));
    this.decBtn.addEventListener("click", this.decrement.bind(this));
  }

  increment() {
    ++this.count;
    this.updateDOM();
  }

  decrement() {
    --this.count;
    this.updateDOM();
  }

  updateDOM() {
    this.countEl.textContent = this.count;
  }
}

new Counter(); 

```

Ok a little better.  The DOM is looking slightly more declarative and we can mess with our CSS all we want now.  We just added super blunt and really poorly thought-out attributes. 

We can make this even better.  What if we set our own standard for DOM querying attributes?  Moreover, what if the values of those attributes meant something too?

Let's enhance our HTML. 

We're going to draw from React and Vue by using something called `refs`.  `refs` are short for "reference" as in DOM reference.  It's simply caching a DOM element in JS.  So let's use a standard `data-ref` attribute:

```html
<div class="Counter">
  <p>Your Count: 
   <span class="Counter__count" data-ref="count">0</span>
  </p>
  <button type="button" data-ref="decrement" class="Counter__btn Counter__btn--decrement">
   -
  </button>
  <button type="button" data-ref="increment" class="Counter__btn Counter__btn--increment">
   +
  </button>
</div>
```

```js
import './Counter.scss'

class Counter {
  constructor() {
    this.count = 0;
    this.countEl = document.querySelector('[data-ref="count"]');
    this.incBtn = document.querySelector('[data-ref="increment"]');
    this.decBtn = document.querySelector('[data-ref="decrement"]');

    this.incBtn.addEventListener("click", this.increment.bind(this));
    this.decBtn.addEventListener("click", this.decrement.bind(this));
  }

  increment(){
    ++this.count;
    this.updateDOM();
  }

  decrement(){
    --this.count;
    this.updateDOM();
  }

  updateDOM(){
    this.countEl.textContent = this.count;
  }
}

new Counter(); 
```
Ok this isn't the worst thing in the world.  The DOM is slightly more declarative.  

Let's address one minor issue:
1. How do we differentiate state fields from DOM fields?

Let's wrap state fields in a `state` object and `refs` in a `$refs` object ( a la Vue):

```js
import './Counter.scss'

class Counter {
  constructor() {
    this.state = {
      count: 0
    };

    this.$refs = {
      countEl: document.querySelector('[data-ref="count"]'),
      incBtn: document.querySelector('[data-ref="increment"]'),
      decBtn: document.querySelector('[data-ref="decrement"]')

    };

    this.$refs.incBtn.addEventListener("click", this.increment.bind(this));
    this.$refs.decBtn.addEventListener("click", this.decrement.bind(this));
  }

  increment(){
    ++this.state.count;
    this.updateDOM();
  }

  decrement(){
    --this.state.count;
    this.updateDOM();
  }

  updateDOM(){
    this.$refs.countEl.textContent = this.count;
  }
}

new Counter(); 
```


But we have at least two major issues:

1. How do we know what object `data-ref` belongs to?
2. How can we get rid of these `.bind` calls?


Enter Babel!

Babel can take modern and proposed syntax and make is ES5 readable.
We're going to rely on two things:  

1. `class-public-fields` https://github.com/tc39/proposal-class-public-fields

2. `template literals` https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

Let's tackle #1:
```js
import './Counter.scss'

class Counter {
  constructor() {
    this.state = {
      count: 0
    };

    this.$refs = {
      countEl: document.querySelector('[data-ref="count"]'),
      incBtn: document.querySelector('[data-ref="increment"]'),
      decBtn: document.querySelector('[data-ref="decrement"]'),

    };

    this.$refs.incBtn.addEventListener("click", this.increment);
    this.$refs.decBtn.addEventListener("click", this.decrement);
  }

  increment = () => {
    ++this.state.count;
    this.updateDOM();
  }

  decrement = () =>{
    --this.state.count;
    this.updateDOM();
  }

  updateDOM = () => {
    this.$refs.countEl.textContent = this.count;
  }
}

new Counter(); 
```


bingo bongo!  No more `bind`!

Now let's tackle #2.
For this we are going to assume we may want to update the attribute name `data-ref` in the future (it's far-fetched, but trust me these refactors happen!)

Let's preface our HTML attribute values with our component name

```html
<div class="Counter">
  <p>Your Count: 
   <span class="Counter__count" data-ref="Counter.count">0</span>
  </p>
  <button type="button" data-ref="Counter.decrement" class="Counter__btn Counter__btn--decrement">
   -
  </button>
  <button type="button" data-ref="Counter.increment" class="Counter__btn Counter__btn--increment">
   +
  </button>
</div>
```
Let's update the JS

```js
import './Counter.scss'

const ref = 'data-ref'

class Counter {
  constructor() {
    this.state = {
      count: 0
    };

    this.$refs = {
      countEl: document.querySelector(`[${ref}="Counter.count"]`),
      incBtn: document.querySelector(`[${ref}="Counter.increment"]`),
      decBtn: document.querySelector(`[${ref}="Counter.decrement"]`)

    };

    
    this.$refs.incBtn.addEventListener("click", this.increment);
    this.$refs.decBtn.addEventListener("click", this.decrement);
  }

  increment = () => {
    ++this.state.count;
    this.updateDOM();
  }

  decrement = () =>{
    --this.state.count;
    this.updateDOM();
  }

  updateDOM = () => {
    this.$refs.countEl.textContent = this.count;
  }
}

new Counter(); 
```

This is pretty darn good so far.  But it's not reusable.  What if we have multiple Counters?  The fix is pretty simple.  We're going to create a `$root` DOM reference.


```html
<div class="Counter" data-component="Counter">
  <p>Your Count: 
   <span class="Counter__count" data-ref="Counter.count">0</span>
  </p>
  <button type="button" data-ref="Counter.decrement" class="Counter__btn Counter__btn--decrement">
   -
  </button>
  <button type="button" data-ref="Counter.increment" class="Counter__btn Counter__btn--increment">
   +
  </button>
</div>
```
Let's update the JS

```js
import './Counter.scss'

const ref = 'data-ref'

class Counter {
  constructor(root) {
    this.$root = root;
    this.state = {
      count: 0
    };

    this.$refs = {
      countEl: this.$root.querySelector(`[${ref}="Counter.count"]`),
      incBtn: this.$root.querySelector(`[${ref}="Counter.increment"]`),
      decBtn: this.$root.querySelector(`[${ref}="Counter.decrement"]`)

    };

    
    this.$refs.incBtn.addEventListener("click", this.increment);
    this.$refs.decBtn.addEventListener("click", this.decrement);
  }

  increment = () => {
    ++this.state.count;
    this.updateDOM();
  }

  decrement = () =>{
    --this.state.count;
    this.updateDOM();
  }

  updateDOM = () => {
    this.$refs.countEl.textContent = this.state.count;
  }
}
```

Now we can instantiate multiple Counters like so: 

```js

const counters = Array.from(document
.querySelectorAll('[data-component="Counter"]'))
.map(element => new Counter(element));
```

So there is a framework-less way to make components.  You can prepopulate your DOM using HTML fragments/mixins/partials (whatever your template language refers to as "chunks reusable of HTML".

There are obviously some bigger things to deal with here:
Passing state in, scoping components, etc.  And that's where I've made a small 2kb library for handling all those things and more without you having to manually scrape any DOM and bind any events. You can declare it all in your HTML and let the library take over.

Check it out.  Let me know your thoughts! I find this is a pretty decent solution for enterprise applications:

My Library for Handling Above Code and MORE!
https://github.com/tamb/domponent

And here is the end result of what we just made:

https://codesandbox.io/s/component-example-ij1zs