Hacker News new | past | comments | ask | show | jobs | submit login
How to avoid rewriting your JavaScript application (polyconseil.fr)
103 points by mrnicehands on Oct 20, 2016 | hide | past | favorite | 62 comments



The article opens with:

>Frameworks, libraries, package managers, guidelines… even languages and meta-languages themselves are changing so fast that there's little to no chance a common or standard way of building applications emerges.

...and then gives an example using React, JSX, and ES6 modules. This is just my opinion but I think React + JSX will look about as antiquated and irrelevant as jQuery in less time than it took jQuery to lose favor among web developers, rendering the argument for longetivity moot.

At least jQuery solved the practical problem of non-standard JS/DOM implementations, but now browser vendors have finally made basic things work among each other, making it irrelevant in most modern browsers. React solves what? Encapsulation? There's Web Components v1 right around the corner, and dozens of competing frameworks that may take its place as the most hyped framework.


The fundamental problem solved by React is being able to express a UI as a function from Application State to DOM, and not having to worry about manually computing the changes to the DOM that need to occur to reflect the new state of the world.

It changes the fundamental question of UI development from "How do I get from where I am to where I want to be?", but "Where do I want to be?"

The funny thing about the particular example in the article is that React was heavily inspired by event loops used in Game Development.


So.. data binding? Like backbone did, and knockout?

I know React has some things going for it, but data binding on the web is an old trick.


IMO React is popular because it forces people to understand MVC. I've actually never seen a Backbone app where views render based on the model. It's too easy to just ram in jQuery in the render function. A lot of new frameworks just try to reduce Backbone's boilerplate of manual model-view event binding in different ways, dirty watching, virtual DOM, etc...

Even flux is basically organized pub/sub. Instead of shouting out what you're doing, you're logging your actions in a logbook. Also, instead of arbitrary actions, you can only combine certain pre-approved actions. It's a natural extension from using globals (too messy) to shouting out your intent while basically using globals (too noisy, still kinda messy) to what I've described above. (still kinda messy, but can be traced and unit tested easily)

Flux/Redux and whatever still doesn't help you solve problems that truly reduce complexity like how to organize scope or what abstractions to use.

There are tons of good frameworks out there, and it's rather disappointing to me that people don't realize that the basics are the same and insist on everyone jumping on the same bandwagon (that just happens to be supported by a rich large company).


Not really "binding" per se -- mostly because "binding" implies you're attaching events to objects and thinking about "changes", and object lifecycle is still something you're concerned with.

React is more functional in nature -- A react component is, almost entirely, a function that takes arguments and spits out DOM elements. React then diffs the desired DOM with the current DOM, and computes the set of transformations needed to update the UI to match the new state -- i.e. "Add a few rows to the table, change the text in a particular span equal to 'boom'" or whatnot.


That's just an implementation detail. All these declarative UI ideas are variations of sprintf. Something, somewhere, is going to do a diff no matter what, whether it's a virtual DOM or the browser itself.


Try making a web app rerenders the DOM on any state change, it will be extremely slow--the browser doesn't diff. :) That's why vDOM & diffing is necessary. Also so UI inputs don't lose focus / selection state / etc.


State change, eh? There's this, which diffs only local updates and doesn't use a vDOM: http://simulacra.js.org/

Disclosure: I'm the author.


Tracking changes to application state & translating to DOM changes is nothing new, [1] it's what Angular, Angular2, Riot, Knockout all do, and doomed to efficiently translating any complex state changes. [2] Not only that, feeding application state in from an API will cause the all the dependent UI to completely rerender.

[1] https://github.com/sapeien/simulacra/blob/master/lib/bind_ke... [2] https://news.ycombinator.com/item?id=12757722


You don't need vDOM/diffing for many types of applications. Your CRUD app doesn't need to update at 60 FPS.


Diffing is an implementation detail. Imperatively updating might still be needed to hit that 60 FPS ;D

Personally, I lean toward server side web apps ATM--users are familiar with page reloads, and developers are familiar with building them.

This is changing--users want faster UX, designers want fancier UI, PMs want cross platform apps. These external wants have developers creating & familiarizing with simpler ways to achieve them.


Data binding exactly, like binding a declarative formula in Excel. Not like Backbone, which did event binding, invoking the programmer's code to imperatively update the DOM.

Knockout, and Angular, both do data binding, but their implementations fall short on complex application state changes & how it translates to DOM changes--leading to incidental complexity: digest loop, TTL, $watch, observable, custom handlers.

React stupidly doesn't try to understand how the state changes translates DOM changes, and instead rerenders, diffs, and applies the update. React's declarative model is also why it can render on the server side, iOS, or Android.

Still, React doesn't translate list sorting to DOM changes efficiently, so imperatively optimizing is still sometimes needed, but less often than in Angular & thankfully not complected by the DOM. React will be replaced when someone solves this. Most likely needing a new programming language, to analyze the application logic & automatically translate into efficient UI updates.


Good description of event loops in games:

http://gameprogrammingpatterns.com/game-loop.html


I think you just described one way of updating things on a web page. The paradigm which React uses is popular in this moment, but there are many other ways to update things on a web page. It does not actually do anything that couldn't be done before, or with other frameworks or libraries.


For me, tools that I find useful are ones that make something no longer be my problem as an application dev. For JQuery, it was low-level browser implementation details. Sure, browsers were different, I just didn't need to care anymore.

For React, it's "Calculating what are the DOM changes that need to happen to ensure the UI reflects the state of the world". In practice, this means that I no longer need to be concerned with "What's the current state of the UI", and I'm not manually diffing things. To a large extent, it makes client-side UI code look a lot more like server-side UI code.

State is one of those things that's a PITA to reason about -- the less of it that I need to care about, the easier it is for me to make code that works. Reasoning and testing in one dimension (space) is a whole lot easier than reasoning in two (space and time).


Using jQuery you can express a UI as a function from Application State to DOM? I'd be really interested in an example. I've only ever used jQuery to do lots of imperative-looking DOM state changes.


It is not common, easy, efficient or idiomatic, but you can: Imagine that in each event handler, instead of doing something, you just update some state somewhere, and after that call a common "draw" function. Inside the "draw" function you redraw all your UI: apply effects, etc. That's it, you have it.


Along these lines, this is why Dan Abramov's Getting Started with Redux[1] is one of my favorite tutorials to anything, ever. The concepts are simple, and half the tutorial is walking through creating a naive implementation of Redux. The actual library, of course, involves performance optimizations and better edge case handling, but at the very least, having a "this is sort of what the innards look like" is a really nice start to approaching anything.

[1] https://egghead.io/courses/getting-started-with-redux


The end user doesn't know or care about implementation details. It's bikeshedding that nobody cares about except the developers themselves. Facebook could have built their web app with Angular or Ember or whatever and nobody would give a shit except developers. Even as a developer I don't give a shit about what framework a web app I'm using is made with as long as it works (if it's horribly broken then I know what to avoid).


Isn't that like saying I'm not an architect so I don't really care what kind of foundation my house is built on as long as it hasn't fallen down yet? They're all held up by the same laws of physics anyway.


Jquery still solves a lot of the problems and saying bloat about is nonsense.

Nowadays everyone embeds mega of worth libraries to build a damn blog. But using jquery to do some basic Dom changes, Ajax and other simple stuff (which you would write you own helper functions for anyway) is bloat.

Long live jquery.


Not to mention the syntax is nicer. Compare the wordy:

> document.getElementById('myid');

to

> $('#myid');

I feel the same way about underscore. Regardless of the helper functions, readability goes up.


So, jQuery instead of just...

  function $(sel) {
    switch (sel[0]) {
      case '#': return document.getElementById(sel.slice(1));
      case '.': return document.getElementsByClassName(sel.slice(1));
      default: return document.getElementsByTagName(sel.slice(0));
    }
  }
I mean... "readability".


And in the end, you add 10 of those selectors. Then you create a small library so they are all at the same place and handy. Then few fixes to support that one browser in that case. And in the end you have jquery.


With that you would end up with something more like Sizzle[0], not necessarily jQuery.

[0] https://sizzlejs.com/


I think you're forgetting additional HTTP functionality, rudimentary animations, additional wrappers around existing JavaScript representation of DOM nodes, and a variety of other things that you'll never realistically used but thrown into all your applications for posterity. Because in the end, you had jQuery.


With http2 the additional request is not a problem. If you don't use jquery, you don't care about old browsers/OS not supporting them either. Also, cdn+caching of the library.


Bytes are bytes are bytes, http/2 or no. It's far less intensive to polyfill what features I actually am using than sending down all of jQuery, and that can still be minified, gzipped, cached, delivered via CDN, and cache-controlled and/or placed in the application cache via service workers (where available).

But I can understand that it's less about the user experience and more about the developer experience nowadays.


in HTML5 would be sufficient: const $ = document.querySelectorAll.bind(document);

(but it would be only pseudo jQuery, the way you can have pseudo React just by using functions and ES6 template strings https://jsfiddle.net/vug86rn0/ )


  var $ = document.querySelectorAll.bind(document);
  $('#myid');


jQuery is mostly superfluous these days, anyway. Javascript supports most of its functionality natively in the browser. Maybe it's time to ditch the overly large generic libraries and start writing in plain old javascript again.


Great article, but i think the op needs to take a look at redux. The best way to use react is to have as little local state as possible. Once you accept that the next question that comes to mind is where should i put all the logic. This article does a good job enumerating the libraries that have been created in an attempt to solve this question [1]. Also when you have all your state in a single global map, you start wishing it would behave more like a database.

The problem with the redux react ecosystem is that it wants to be functional and so it imports a lot of innovations done in functional languages, but in the end js it's not functional and you end up fighting the language. For example the reselect library [2] tries to implement selectors, but without native inmmutable data structures its too painfull to use.

I see a lot of people trying to implement so much features that should have been part of the language as libraries and i ask myself why not just use another language that has all those features, i mean they are all compiling to javascript anyways. Its madness.

[1] https://medium.com/@jeffbski/where-do-i-put-my-business-logi... [2] https://github.com/reactjs/reselect


Uh, this code is kind of crazy. Even in the "fixed" example. Why is he calling instance methods on components? Why is he muddling with jQuery? And the global click events? He started with bad principles and just implemented them better.


While working on a ReactJS/Redux reducer, I had the same epiphany. "This is basically the old Windows main() message pump!"

Redux really is a beautifully elegant way to decouple global state and actions from the UI.


Yep - there was an article last year that pointed out the distinct correlation between a classic Win32 WndProc and a React+Flux-style architecture: https://bitquabit.com/post/the-more-things-change/ .

But yeah, as I've said in a few recent comments, Dan and Andrew made some excellent decisions while designing Redux, and those decisions have helped Redux hit a sweet spot for concepts and use cases.


Thanks for linking to the article. I remember reading something like that but had no idea on how to find it.


Interesting article, but I cant help but think every one of those points is 'fixed' in EmberJS (correct me if I'm wrong!). Central stores, central place to fetch 'models' (data over Ajax), dependency injection so it's all mockable, a run loop, simple and understandable data flows.

The central store and the run loop is very powerful. If you have two completely unrelated components issue two fetches for the same model type but different IDs (ie two different blog comments) Ember coalesces them into a single request (I.e /api/comments?id[]=1&id[]=2) transparently. That's awesome.


isn't the jsx also wrong? you can't have 2 separate div's like that in react right? you'd have to wrap them in another div.

render: function() { return ( <div className="title">{this.state.title}/> <div className="button" onClick={this.handleClick}> ) }


yes, that will break.


The idea here is sound. It's essentially a reinvention of one way data flow in Flux: data flows down to components, events flow back to a central point (dispatcher in Flux, main.js in this example).

His implementation is a bit iffy though. How will his app look when there are dozens of components? A single main.js module with dozens of data fetching functions is going to be really hard to work with.


It's typical MVC application, reinvention of TurboVision.


I think this is the motivation behind Cycle.js

https://cycle.js.org/


I hadn't heard of cycle.js before, but looked into it a bit just now and it seems pretty nice.

My only concern would be that I hadn't heard of it before, so I wonder if it has significant traction and production readiness.

Anyone have thoughts on that?


For managing side effects, redux-saga[0] is god-send if you are using redux.

[0]: https://github.com/yelouafi/redux-saga


The idea behind redux-saga, to push all effects to the edge (handled by a runtime), only handling descriptions of the actual effects doesn't have to be limited to redux/redux-saga either. You can leverage generators + a runtime to build all kinds of applications like this [0] (you can sort of do this without generators too, but it's a bit more quirky when you cannot control/pause the execution [1]). Easy testing (no more mocks/stubs in the traditional sense - only data/descriptions) and predictability are just some of the benefits.

[0]: https://gist.github.com/eiriklv/a498112b3a73fc2b92975649924c...

[1]: https://gist.github.com/eiriklv/b6cc681447a0939365e1ba044fac...


Isn't this why you should try to make your components as stateless as possible?


Another option, rather than going back to client-server style programming, is that you could write your web app like it's 2004 and leverage the unique network architecture it had (REST/HATEOAS):

http://intercoolerjs.org/2016/05/19/back-to-the-future.html

That is probably the biggest issue: no one cares about long-term support or maintainability of UI applications nearly enough to actually create the ecosystem that it deserves.

Absolutely. Not a lot of long term, slow thinking going on these days.


Please, can you stop promoting your lib in every topic about JavaScript...


I try not to unless it is reasonably relevant. In this case, the author is suggesting a way to deal with the fact that we are building apps like we were back in the 90's, but in javascript, and I have an alternative suggestion, which is that we build them like we did in the 00's.

But I appreciate that people who have seen it before are sick of seeing it again.


REST/HATEOS is an entirely orthogonal issue to the structural issues of the javascript code that he is discussing.

So are the models referenced in the image you link to.


Orthogonal to the structural issues, yes, but not to the original premise, which is the problem of rewriting JavaScript UIs. It seems fair to extend the discussion to ways to avoid writing JavaScript UIs in the first place.


The best way to avoid writing JS UIs is to invent a time machine and kill Brendan Eich before he puts it in Netscape. People prefer the interactivity that it enables, and no amount of server-side architecture is going to replace that.


I should have said write less JavaScript, not avoid. Still, the tradeoffs of interactivity and client-side architecture required to keep it sane seems like an ok discussion to have.


While this is probably true, I think this argument is used to defend things that could totally be handled without any (or with very minimal) javascript. Turbolinks or Pjax will get you most of the way toward the interactivity people are used to with single page JS apps. Of course there are interactions that necessitate more Javascript. In my experience, those cases are the exception, not the rule.


Nice article but i think it touches two problems, but then offers a solution to one.

Every program has a code structure. Certain programs have better code structure than others. These are properties independent from the programming language. Javascript evolved from a single entry point, being the [in]famous $.ready() to set behaviors of some html elements, to full blows ES6 single page applications.

It all started as a toy language.

But it simplicity is also its flaw: it enables every human with a not so deep understanding of computer architecture to write a button that changes color on click. The absence of a type system and a solid class paradigm (introduced in ES6) spoiled programmers to pass any object down to any function breaking well known software paradigms: Law of Demeter (https://en.wikipedia.org/wiki/Law_of_Demeter), Open/Close Principle (https://en.wikipedia.org/wiki/Open/closed_principle) and the Liskov Substitution Principle (https://en.wikipedia.org/wiki/Liskov_substitution_principle).

I'm in the Web space professionally from 15+ years and those are the 3 rules i see JS devs break the most, generating complected code (for more understanding of the term have a look at https://www.infoq.com/presentations/Simple-Made-Easy ), hard to maintain and extend like the example shown in this article.

The advice to build interfaces around data structures, proposed as solution, is no different than the Liskov Substitution Principle.

The other problem the article cites is the event loop.

At the time o $.ready() there was no event loop. Developers were just attaching functions to user events: clicks, hovers, blurs, focus. Just a direct mapping between an element and a function. You can simply come to the conclusion that the trigger and the action to be performed were not loosely coupled, but indeed tight together. Easy, yet not scalable.

Tieing events to the dom structure was another sin opening more questions: should an element that is not interactable fire events? bubble them ? every browser had its own answer to those questions. Things got even more complicated with single page applications which html element in the page can be added and removed. So here comes the event loop, like other well known ui stacks did in the past.

The concept of an event loop is not a novelty, it is indeed bound to the architecture of our computer: clock cycle, interrupts, kernel events. In the case of windows is the well known WPF (https://en.wikipedia.org/wiki/Windows_Presentation_Foundatio...) which has, among a lot of other things like any Microsoft product, the concept of a dispatcher that is central to the flux architecture.

In 2015/2016 with React/Flux Javascript and the Web is moving out of puberty, enabling developers to write clean, decoupled, extensible code. Thus no all devs are ready to grasp those architecture that are so obvious in other ecosystems. To cite Poul-Henning Kamp in A generation lost in the bazaar (http://queue.acm.org/detail.cfm?id=2349257):

"So far they have all failed spectacularly, because the generation of lost dot-com wunderkinder in the bazaar has never seen a cathedral and therefore cannot even imagine why you would want one in the first place, much less what it should look like. It is a sad irony, indeed, that those who most need to read it may find The Design of Design entirely incomprehensible."

my 2 cents


Or code like its 2016 and use something like what its proposed by elm, clojurescript's reagent or posh libraries.


> JavaScript application

Note how it doesn't say "Elm application" or "ClojureScript application".


I was talking about the concepts not the languages. There is even an elm paper [1] and the README in the reagent repo is great as well.

[1] http://elm-lang.org/papers/concurrent-frp.pdf


Redux does this.


Unfortunately a year is never an argument.


They change the original title i was just making a reference to that.


I see.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: