React.js vs traditional MVC (Backbone, Angular)2014-04-14
I've been working with React for the last two weeks, and I think it's quite a revolutionary library. In this blog, I will tell you why.
If you don't know what React is, visit the doc and read up on it. The basic idea is this:
Since DOM manipulations are slow, React replicates the DOM virtually. Your application talks to the virtual DOM that is very fast, and then React diffs the virtual DOM with the real DOM and applies all changes efficiently.
In case you wonder how this fits into todays MVC landscape, let me quote the react guys: "Many people choose to think of React as the V in MVC."
I personally think that this is a conservative statement that should appeal to people who don't like to try new things so much. If I interpret the statement correctly, they themselves don't think that way.
React's way of doing things is a lot more than "the V in MVC". React's fast, virtual DOM lets you do things that were so out of the question before, most people will not be able to fully grasp the big advantage of it for a while.
A real-life scenario
At my company, we developed a SPA called concat. One part of this app is a list of 5000 companies and their stock returns.
you can filter this list by
- text search
- whether they had news over a given time period
other features are:
- custom favorite lists per user
- a date range selector
- the filters contain summary stats. For example you see how many companies you have in the list for each country with your current filter-setting.
The app was implemented in chaplin which is a framework based on Backbone. The problem with this list-widget is that whenever a user triggers a filter, a lot of views need to be re-rendered because of the summary stats on the different filters. This needs a lot of controller code.
Now there are tools available to implement this in a sane way. With Backbone you would do the talking between the different parts via events, favorably with a controller receiving all the user inputs, manipulating the models if needed and then calling the views and tell them to re-render if necessary.
There are other frameworks that provide simpler ways of doing this (2-way data binding), usually at the cost of flexibility.
The question is: Is there generally a better way? Or let's rephrase the question: What would be the best way?
How about this: "When a user interaction occurs on the stock-list, re-render everything"
This is the intuitive thing to do. Im sure this is what naive, inexperienced devs would try first. It's the simplest and therefore easiest approach. However, everybody knows this won't work due to render performance. With React, this suddenly becomes a viable design pattern.
Think about what this means for a while. You can rerender everything all the time. This means that half of the work - the more complicated half - will simply vanish, because you don't need to develop something that orchestrates the partial rendering and handles all the side-effects carefully.
Let me quote myself from before:
The problem with this list-widget is that whenever a user triggers a filter, a lot of views need to be rerendered because of the summary stats on the different filters.
With React, the problem is:
The problem with this list-widget is that whenever a user triggers a filter, I re-render everything because of the summary stats on the different filters.
This sounds a lot easier doesn't it? Now different frameworks provide similar simplifying solutions for some specific scenarios. But React provides them ubiquitously. The virtual DOM is always there.
I have rewritten the stock-list in React, and I have been blown away. Things become so much easier to build and understand. Things tend to simply go away:
- Backbone Events: I have yet to see a case where I would need them. (I'm sure there are some)
- Backbone Models/Collections? Plain objects are enough.
- Memory leaks because of non-disposal of Views? Gone.
- My complex controllers are nicely split up.
- Except for ajax stuff, I didn't need jQuery so far.
- My application state is minimized (!!!).
- My code is clean, organized and extensible without any major refactoring.
- It took me less discipline to not make a mess (global publish/subscribe à la Backbone.Events is almost as evil as global variables)
Also, I have never felt "restrained" by React. It doesn't trade simplicity/robustness for flexibility like most frameworks out there.
You need to fully embrace the possibilities of React though. I've seen some posts where people just switch Backbone Views with React. This can make sense in a transition/try-out phase. But if you start a new project, I suggest to throw everything out of the window first and try to fully embrace React. I think not much else (except a Router) is needed besides React to build a complex and fast SPA with a clean, simple and maintainable codebase (Obviously there are exceptions to this).
Your code will not contain the usual MV* patterns. Don't be scared of that. Code will be organized. Concerns will be separated. And so far, I feel like the React way is superior to MV* for a lot of use cases. You could easily marry the two if push comes to shove, but: It's likely you'd want to do this because MV* is familiar, not because it improves your codebase objectively.
There is one thing that sucks a little with React.js: Talking to the virtual DOM differs from talking to the real one (JSX, coffeescript craziness). But it's a trade-off that I'm willing to accept in a heartbeat.
If you'd like to know how React works in practice, look at this little tutorial, it taught me the most out of everything I've read.
One thing is for sure: React does things so differently, you can not judge it from a distance. You need to try it extensively in real-life to figure out if it works for you.