/ javascript

There Is No State

A recent article by Alberto Gimeno suggests the reason modern JavaScript frameworks exists is the difficuly to keep state in sync with the UI. I hope to convince you that difficulty is fake: it's only hard if you think like a framework developer. Their advantage lies elsewhere.

Coding Like a jQuery Developer

Alberto's main argument is displayed using a code snippet that is meant to show why keeping the state in sync with the UI is hard:

See the Pen vRZLrq by Alberto Gimeno (@gimenete) on CodePen.

But already this code does not reflect how we wrote code before there were frameworks. Nobody kept state in an external object, nor used classes to build user interfaces.

A more typical implementation of the same program would look something like this:

See the Pen EEpxKK by Ynon Perek (@ynonp) on CodePen.

It's less than half the size of the original program and does almost the same thing. It also doesn't use classes just simple jQuery.

One important difference is that I dropped the concept of "state". Application state is now implicit in the DOM and all JavaScript code does is to modify the DOM in response to user events.

The difference is best shown in the method updateHelp that is typical to modern framework developers:

  // utility method
  updateHelp() {
    if (this.state.length > 0) {
      this.help.classList.add('hidden')
    } else {
      this.help.classList.remove('hidden')
    }
  }

To a jQuery developer this method is pointless: You either deleted the last item so you'll want to show the help text; or you created the first item so you'll want to hide that text. Checking the state and "rendering" it are modern concepts.

Negligible Differences

There are some negligible differences between the two programs that are easily fixed if needed, but we should still be aware of.

The first is an optimisation in the original program caching all UI related objects. In my experience with old jQuery code many programs didn't use that practice. I started seeing it around the time mobile web became an thing (around 2012) because mobile devices are slow. For many applications today's browsers and hardware is fast enough not to worry about caching. Clearly as programs grow larger it's a good idea to use getter methods, object oriented style and caching to improve readability and performance.

A second difference is the assignment of a timestamp to items and saving it externally (in state). jQuery provides a $.data function to keep data related to DOM nodes, and modern browsers provide the dataset API to keep data inside DOM nodes. To keep an ID inside items a jQuery developer may use:

$('li:last-child').data('id', String(Date.now()));

Important Differences

There is still one very important difference I chose to ignore throughout this post, and that is the scope.

In the jQuery version most of the selectors are global. This is a property of a lot of jQuery code I reviewed (and wrote) during my work. Global selectors become confusing as your program grows. They lead to long methods and code that's hard to understand.

Most of all global selectors stand in our way when we want to create sharable components for large applications.

jQuery developers worked around that limitation by writing jQuery Plugins. Unfortunately jQuery had no mechanism to keep the plugins scoped and so the more jQuery plugins you used, the higher your chances were to add memory leaks or other weird bugs to the application.

Modern frameworks' main concern is building a component based architecture. The goal is to write and test each component separately and then have all of them play nicely in the larger application.

State management is just a part of that story, a part not all frameworks agree on. Yes, saving a component state in JavaScript may help in testing and development. Yes, it may yield better performance in some situations. But it's not the deep important reason modern frameworks exist. An interesting framework in this respect is the recent StimulusJS which provides component based architecture but saves all state in the DOM.

And yes saving the component state in an external object sure helps when you have several components that need to show the same data, as it happens in large scale modern JavaScript applications. But it's not the only way.

Keeping the UI in sync with the state is hard only if the state is managed as an external object that one always needs to "render" into the UI. The thing is, that's not how we wrote code in the old days before the frameworks.