JavaScript Frameworks — Why to use them at all

I’ve written a lot about JS frameworks over the last year or so. But one thing I haven’t really covered is the step one: why to use them at all.

One of the biggest issues for junior developers is looking over a bewildering ecosystem of competing, complementary or subtly different options and trying to figure out what they actually need to use. The root cause of this is an article of its own: junior developers treating web development tools like a checklist for employability, rather than solutions to actual problems.

We can get a better view of the JavaScript framework world by looking at what JS frameworks are for, what problem they attempt to solve, and how they do so.

If you ask people, you’ll typically get wide ranging answers, including things like reducing code, keeping structure clean, reusable elements, etc. But really these things are side-effects. The actual purpose is simple. They are to map the application state to the DOM.

What that means is most clearly demonstrated by showing the opposite, and the best way to do that is to show some jQuery.

These days jQuery is a much maligned library, but it still excels at direct manipulation of the DOM. The problem is we’ve sort of woken up to the fact that direct manipulation of the DOM is how you get cooties. Doing it well just gives you cooties faster.

To make the point, we’ll have a very simple bit of markup with jQuery that acts as a sort of custom radio button. I’m not going to bother making this any good, just wanted to the intent clear.

<div>
<span class="btn btn-primary toggle-button">Pigs</span>
<span class="btn btn-primary toggle-button">Sheep</span>
<span class="btn btn-primary toggle-button">Wolves</span>
</div>

<script>
$('.toggle-button').click(function(){
$('.toggle-button').removeClass('active');
$(this).addClass('active');
});
</script>

There are problems here. It’s not elegant, but it serves. First of all, there’s no real meaning to .toggle-button. It’s entirely arbitrary. Does it have styling associated with it? Maybe? Maybe not? Removing a styling class will change behaviour — not ideal. Is it really the right thing to select on? Is this really what classes are for? Should we have used ids? We could use data attributes, but then we’re littering the markup with those as well.

A bigger problem is how do we get the name of the animal selected? We have to do some pretty gnarly DOM checking.

let animalSelected = $('.toggle-button.active').html(); 

This solution is error prone, and not very resilient by design. Two things could be active. Or none. It might change when someone changes a class, or when another set of toggle-buttons is added.

This issue of having a clear “value” is commonly fixed by storing that value somewhere, such as a hidden form field. Doing this also makes it easier to submit the data as a form.

<input type="hidden" name="animal" id="animal" value="">

And in our javascript event we can add in the following to get the animal value.

$('#animal').val($(this).html());

This makes the interface handling slightly better. If by “better” you mean “quite a bit worse”. Even though we’ve fixed one issue, we’ve created a worse one. For a start, there’s markup here now that may not fill any real purpose other than as a bin for a number. But there’s a deeper issue.

Let’s say we have another function somewhere that sets the animal — we got that data from Ajax request for example, or it was loaded as part of editing. The value of the hidden input is correct, but none of the animals appear selected. Your application state is now up the shit. To use a technical term.

As the size of an interface or application increases linearly, the potentially for this sort of interaction and error rises exponentially. Having to keep the interface in sync with the underlying data becomes incredibly tiresome, unreliable, and error-prone. If only there was another way.

Let’s look at an equivalent React component. The same applies to any similar framework (Ember, Angular, etc) or library (Vue), but React provides an easy way to demonstrate in a self-contained example.

export default class AnimalSelector extends component {
constructor(){
this.state = {
animals: ['Pigs', 'Sheep', 'Wolves'],
selectedAnimal: null
}
}
  selectAnimal(selectedAnimal){
this.state({selectedAnimal});
}
  animalButton(animal){
const activeClass = this.state.selectedAnimal === animal
? 'active'
: '';
    return (<span 
class={`btn btn-primary ${activeClass}`}
onClick={() => this.selectAnimal(animal)}>
{animal}
</span>)
}
  render(){
return <div>
{this.state.animals.map(animal => this.animalButton(animal))}
</div>
}
}

There are things wrong with this React, think of it more as React-like pseudocode, because I don’t that much care as it’s 3 am and I’m watching 90s movies until I can’t avoid sleep any longer.

The structure of a React component is relatively simple — the data that creates the interface is held in the state property. Drawing the component (with render()) uses that state to make decisions about how to render elements. Changes to that state (all done using this.setState) cause the component to re-render, remaking all the previous layout decisions. This “single source of truth” for how thing should look and behave is critical, and what we were missing in our jQuery examples. Fundamentally all of these elements are “bound” to the state.

The fact is, none of the potential issues listed in the jQuery example above will occur in this React example. We haven’t tried to avoid them. They’re just inherently not problems. And they won’t become problems.

This isn’t to say that there aren’t issues that can occur. Actually managing that state properly and consistently, and especially sharing it across components, can be difficult. This is the problem tools like Redux and Ember Data so elegantly solve.

Application architecture is a complex thing, and this view binding side of things is only a small part of it. It’s one that JavaScript frameworks do in surprisingly similar ways, despite their differing syntax. Larger application patterns differ more significantly and it’s for these reasons (which are out of scope of this article) that I prefer tools like Ember. But the how and why to bind state to the DOM is something with near universal implementation, from Backbone to Angular 6.

There will always be people who say you don’t need frameworks, that native JavaScript or jQuery is all you need. People interpreted my All Frameworks Are Terrible article as saying the same thing, but what I was actually criticising was the tribal and misguided way we talk about JavaScript frameworks.

There are two types of people who look down on JS frameworks and claim they’re not necessary.

Some are people who simply haven’t done complex enough applications to have run into the issues and inconsistencies that are inherent with direct DOM manipulation approaches. (This is particularly common with people who develop primarily backend application with minimal JavaScript.)

Then there are people who are building shitty, error prone software, but who lack the knowledge, humility and awareness to realise they’re doing it.

It’s important that you know what these frameworks are for, what problem they solve. Because that’s critical to making informed decisions about which one to use. That you ultimately need to use one of them is a given, it’s not a matter of if, but when.