A short history of Javascript frameworks: a comparison of JQuery, AngularJS and React
Javascript frameworks come and go -- by the time you have finished reading this post, three new frameworks will have been released. While it may sometimes look like they're just introducing more syntax to learn, the ones that actually get popular often introduce new paradigms that allow us to build features more quickly and with fewer bugs. We can learn a lot by taking a step back: where did we come from, and where are we now, in the year 2016? Which problems do the big frameworks solve, and which are left as an excercise for the programmer?
At first, there was JQuery
With an easy-to-learn syntax, JQuery led to the first bits of interactivity being added to our webpages. If you knew CSS, you knew enough to manipulate your webpage on the client side. Making a button click trigger the addition of a list item would typically look something like this:
$('.button').click(function(){ $('ol.todos').append('<li>New item!</li>'); });
No longer were full page refreshes necessary for every small change, and users rejoiced. That said, this style of programming can quickly become unwieldy. As an example, let's consider that we do not only want to show the new list item to the user, but also to save it somewhere. This requires that we keep an internal model of the list. In turn, whenever the list is modified, both the view and the model will have to be updated:
let todos = [] $('.button').click(function(){ $('ol.todos').append('<li>New item!</li>'); todos.push('New item!'); });
It's easy to see how quickly this will lead to the introduction of bugs: as soon as the developer forgets to update the view when the model is updated, or vice versa, the user will be looking at incorrect data.
Enter AngularJS
Then, Angular came onto the stage, ushering in the era of actual web applications. It came with strong opinions on how to structure your projects, removing a lot of the error-prone paperwork that we now associate with JQuery. In Angular, your app's components consist of controllers and templates, with the former responsible for manipulating the state of your model and the latter for rendering it to the user. Thus, our little example now looks like this:
<ol ng-controller="TodoListController as vm" class="todos"> <li ng-repeat="todo in vm.todos">{{todo}}</li> </ol>
function TodoListController{ let vm = this; vm.todos = []; vm.addTodo = function(){ vm.todos.push('New item!'); }; }
Angular eliminated a complete class of bugs: you can safely use vm.todos
, and it would contain exactly the same list as the one the user is looking at. It quickly grew to be extremely popular, and continues to be widely used to this day.
That said, Angular isn't perfect either. Consider the case where we have two components: a list component, that renders a list of items, and a menu component, that allows the user to navigate through your app.
How can we make sure the menu component knows about the number of list items? We can e.g. use the Angular concept of services: a single place to store your models, accessible by all your controllers. Our list controller might now look something like this:
function TodoListController(TodoListService){ let vm = this; vm.todos = TodoListService.getTodos(); vm.addTodo = function(){ vm.todos.push('New item!'); TodoListService.updateTodos(vm.todos); }; }
However, this looks suspiciously similar to the problem we had with JQuery: we are now responsible for keeping things in sync again: the TodoList component's internal model and the service's model. This makes us vulnerable to bugs in which one part of your application displays data incongruous with that in other parts of your application.
An alternative approach would be to have your controllers manipulate the service's model directly. This tight coupling, however, comes with its own set of problems in which changes in one part of your application might have unintended consequences in others.
React to the rescue
While Angular remains a popular choice for web applications, an alternative solution has quickly been embraced by a significant number of developers: the combination of React and its trusty sidekick Redux.
At the core of the style of programming that characterises this combination is the embrace of immutable data. Typically, a React component is merely a function that describes what, given the state of your application, your view should look like:
state => view
To emphasize: the component does not alter your models (state
), but merely defines what should be shown to the user (view
). When our TODO list component is given an array of four items, its render function will return an <ol>
containing four <li>
s. When the same list is provided to our menu component, it can return the text TODOS (4)
.
The advantage of not manipulating your models in your components, is that there is no risk in sharing the models between components. The menu component can safely access the model of the list, since it cannot manipulate it and thus cannot cause inadvertent effects in the list component. When the model is changed, the rendering function is simply called again with the updated model, and React makes sure that the new view will be displayed to the user.
Every component now always has access to an up-to-date version of our models, and so does our view. Redux helps you managing updating your models. You define the possible actions a user can perform, and a function that describes how those actions affect the state:
(currentState, action) => newState
Again, note that this function uses immutable data: it does not modify currentState
or action
, but simply returns a new object newState
that describes what the state should look like given the previous state and the action that was performed. (Apart from safe state sharing among different components, this also enables other cool features.)
What's next?
By taking a birds-eye view of the history of Javascript frameworks, we've seen what each brings to the table, and what problems they solve. Angular largely removed the need to keep your views and models in sync, and React/Redux gives us the ability to safely share our models between our components, while maintaing a proper separation of concerns.
While the idea of learning so many frameworks can be a bit too daunting, simply being aware of the new paradigms they bring to the table can go a long way. While React has seen rapid adoption, Angular is still going strong; thus, you can simply stick with Angular if you're comfortable with that. Nonetheless, the lessons taught by React can help be carried over to Angular, and are thus still useful to study.
No framework is perfect, and neither is React. In my next post, I will look at a remaining pain point, and what trend is emerging to deal with it.
This work by Vincent Tunru is licensed under a Creative Commons Attribution 4.0 International License.