How I stopped worrying and fell in love with React

I offer readers of "Habrahabr" a translation of the article "How I learned to stop worrying and love React" .

If you ask me what I thought about React two months ago, I would say ...
Where are my templates? What is this crazy HTML doing in my JavaScript? JSX looks weird! Hurry up! Burn it!



This is because I did not understand him.

But I assure you, React is definitely the right way. Please hear me out.

Good old MVC


The root of evil in an interactive application is state management. The “traditional” approach is MVC architecture or some of its variations.

MVC assumes that your model is the only source of truth, the whole state lives there. Views are derived models and must be synchronized. When the model changes, so does the view.

As a result, interaction with the user is fixed by the controller, which updates the model. So far so good.


Render views when changing a model

It looks pretty simple. First, we need to describe our view - how it transforms the state of a model into a DOM. Then, whenever the user does something, we update the model and re-render everything. Right? Not so fast. Unfortunately, not everything is going smoothly here. For 2 reasons:

  1. In fact, the DOM has some state, such as the contents of text fields. If you re-render your DOM completely, this content will be lost;
  2. DOM operations (such as deleting and inserting nodes) are really slow. Constantly re-rendering everything leads to terrible performance.

So how do we keep the model and views synchronized and avoid these problems?

Data binding


Over the past 3 years, the most common framework feature introduced to address these issues has been data binding.

Data binding is the ability to keep your model and views synchronized automatically. Usually in JavaScript these are your objects and your DOM.

This is achieved through the ability to declare dependencies between pieces of data in your application. Changes in state will be distributed throughout the application and all dependencies will be updated automatically.

Let's see how it works in practice in some well-known frameworks.

Knockout


Knockaut stands for the MVVM (Model-View-ViewModel) approach and helps implement the View part:

 // View (a template)
<p>First name: <inputdata-bind="value: firstName" /></p><p>Last name: <inputdata-bind="value: lastName" /></p><h2>Hello, <spandata-bind="text: fullName"></span>!</h2>

// ViewModel (diplay data... and logic?)var ViewModel = function(first, last) {  
  this.firstName = ko.observable(first);
  this.lastName = ko.observable(last);
  this.fullName = ko.pureComputed(function() {
      // Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.returnthis.firstName() + " " + this.lastName();
  }, this);
}; 

And voila. Changing the value of any of input will trigger a change in the span. You never wrote code to connect it. Cool, huh?

But wait, what about the fact that the model is the only source of truth? Where does the ViewModel get its state from? How does she know that the model has changed? Interesting questions.

Angular


Angular describes data binding in terms of keeping the model and view synchronized. From the documentation:



But ... should the view communicate directly with the model? Are they closely related?

Anyway, let's take a look at hello world:

// View (a template) 
<div ng-controller="HelloController as hello">  
  <label>Name:</label>
  <input type="text" ng-model="hello.firstName">
  <inputtype="text"ng-model="hello.lastName"><h1>Hello {{hello.fullName()}}!</h1></div>// Controller 
angular.module('helloApp', [])  
.controller('HelloController', function() {
  var hello = this;
  hello.fullName = function() {
    return hello.firstName + hello.lastName;
  };
});

From this example, it looks like the controller has state and behaves like a model, or maybe like a ViewModel? Assuming the model is in a different place, how does it synchronize with the controller?

My head starts to hurt a little.

Problems with data binding


Data binding works great with small examples. However, as your application grows, you are likely to encounter some of the following problems:

Declaring dependencies can quickly lead to a loop


The most common task is to deal with side effects from a change in your state. This picture from the introduction of Flux explains quite clearly how the hell of dependencies begins to creep:



In this case, can you predict what changes will happen if one change occurs in one model? It is very difficult to talk about code that can be executed in a completely arbitrary order, when any dependency is changed.

Pattern and display logic artificially separated


What is the role of the presentation? Presentation of data to the user. What is the role of ViewModel? Presentation of data to the user. What's the difference? No.

In the end, the presentation component must be able to manipulate its data and present it in the desired format. However, all template languages ​​are essentially disabled: they can never achieve the same expressiveness and power as the code.

Quite simply, {{# each}}, ng-repeat and databind = “foreach” are all a bad substitute for the native and trivial for loop in JavaScript. And they cannot go further. No filter or map.

Data binding - hack around rendering


The holy grail of simplicity is not discussed. Everyone always wanted to re-render the entire application when the state changes. Thus, we could stop dealing with the root of evil: states change over time - we could simply describe that our application represents any particular state.

Facebook React Input


Turns out they did it. React implements a virtual DOM that kind of gives us the Holy Grail.

In any case, what is a virtual DOM?


I'm glad you asked! Let's look at a simple React example.

var Hello = React.createClass({  
    render: function() {
        return<div>Hello {this.props.name}</div>;
    }
});
React.render(<Helloname="World" />, document.getElementById('container')); 

This is all you need for a React component. You must have a render method. Difficult, huh?

OK, but what is the <div>? This is not JavaScript! Definitely not him.

Your new friend, jsx


Actually, this code is written in JSX. A super javascript suite that includes parenthesis syntax for defining components. The code above, when compiled in JavaScript, will actually become like this:

var Hello = React.createClass({displayName: "Hello",  
    render: function() {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});
React.render(React.createElement(Hello, {name: "World"}), document.getElementById('container'));  

Did you notice createElement calls? These objects make up the implementation of the virtual DOM.

Pretty simple: React first collects the entire structure of your application in memory using these objects. It then converts this structure to the current DOM nodes and inserts them into the browser DOM.

Ok, but what's the point of writing our HTML with these weird createElement functions?

Virtual DOM - Fast


As we have already discussed, DOM changes are ridiculously expensive, so the DOM should be changed as few times as possible.

The React's virtual DOM, however, does it really fast by comparing two trees and finding exactly what has changed between them. In this way, React is able to calculate the minimum number of changes needed to update the DOM.

Practically speaking, React can compare two DOM trees and calculate the minimum set of operations that need to be performed. This means two things:

  1. If text fields with text are rendered, then React expects that there is content and will not touch it. No more loss of fortune;
  2. Comparing virtual DOMs is not expensive at all, so we can compare them as much as we like. When diff is ready to actually change the DOM, it will do this with a minimal amount of operation. No more slow layout.

Remembering 2 problems with re-rendering the whole application when changing state?

It has passed.

React Mapit State in DOM


Virtual DOM rendering and diffing is the only magic part of React. Its excellent performance allows us to have a much simpler architecture. How simple?
React components are idempotent functions. They describe your UI at any given time. Just like an application rendered on a server.

Pete Hunt, React: Rethinking best practices

This is all what a React component should really be. It displays the current state of the application in the DOM. You have all the power of JavaScript to describe the user interface: loops, functions, scopes, compositions, a full-fledged template language.

var CommentList = React.createClass({  
  render: function() {
    var commentNodes = this.props.data.map(function (comment) {
      return (
        <Commentauthor={comment.author}>
          {comment.text}
        </Comment>
      );
    });
    return (
      <divclassName="commentList">
        {commentNodes}
      </div>
    );
  }
});
var CommentBox = React.createClass({  
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.props.data} />
      </div>
    );
  }
});
React.render(  
  <CommentBox data={data} />,
  document.getElementById('content')
);

Start using React today.


At first glance, React may seem a bit complicated. It offers very large paradigms, which is always not convenient. However, when you start using it, the benefits become clear. React's

documentation is excellent. You should try it and follow the tutorial. I'm sure you will love React if you give it a chance.

Good coding.

Original article: How I learned to stop worrying and love React
Article author: Guilherme Rodrigues

Also popular now: