Monday, February 2, 2015

What is one-way data binding in React?


As a JavaScript developer who has a lot of experience with jQuery but new to frameworks like Knockout or Angular, when React emphasizes it is a one-way data binding vs two-way data binding of the other two frameworks, I was kind of confused.

React: "React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding"

After a few more reading, actually it is dead simple. One-way data binding is just about not doing something than about doing something.

Let's use jQuery as example to explain what it is. Let's start with a zero-way data binding or no data binding at all.

Zero-way data binding

It is just HTML with no script.

<input class="myInput" type="text" />

 
no script, no data binding. Isn't that simple?
 
One-way data binding
<input class="myInput" type="text" />
<script>
    var data = 'hello world';
    $('.myInput').val(data)
</script>
When the script runs, it one-way initializes the HTML control to a value.

Two-way data binding
<input class="myInput" type="text" />
<script>
    var data = 'hello world';
    $('.myInput').val(data);
    $('.myInput').onchange(function(){
        data = $(this).val();
        //update any other 1000 controls who uses data too    })
</script>
As you can see, after the one-way initializing the input control, the "onchange" event handler updates the data and other 1000 controls who use the data. This the 2nd direction of data flow: from UI to data.

React's Data Binding

The confusion I had is that why they are all saying React is one-way data binding, when definitely you can use same event handler to implement two-way data binding. It turns out that it is called so just because you have to implement two-way binding by code explicitly instead of implicitly as in the other frameworks.

So this React's one-way data binding
var SearchBox = React.createClass({
    getInitialState: function(){
      return {currInput: 'hello world'}
    },    
    render: function(){
        var currInput = this.state.currInput;
        return (
            <div>
                <input type="text" value={currInput} />
                <div>{currInput}</div>
            </div>
        );
    }
});

React.render(<SearchBox />, $('.myContent')[0])
 
The HTML controls are initialized with the state/data at one direction. The reason it is not two-way yet is just that we haven't implement anything that will flow data from UI to state/data.

React's "two-way data binding"

var SearchBox = React.createClass({
    getInitialState: function(){
      return {currInput: 'hello'}
    },
    handleKey: function(e)
    {
        this.setState({currInput: this.state.currInput + e.key });
    },
    render: function(){
        var currInput = this.state.currInput;
        return (
            <div>
                <input type="text" value={currInput} onKeyPress={this.handleKey} />
                <div>{currInput}</div>
            </div>
        );
    }
});

React.render(<SearchBox />, $('.myContent')[0])
Call this code "two-way" is kind of misleading. When you have to do something explicitly by code, it is not "two-way". It is still one way because "two-way" means two direction data flow automatically implicitly by framework. When you have do it by code, it is "your way" not "two-way". Stop playing the word game! :-)

Why it is good?

It is good just because it falls in the pattern that "less unknown is good when complexity grows". When the framework doing a lot for you automatically, implicitly in a big application, you will get lost. For a simpler application, it is just great since you write fewer code.

For example:

return (
    <div>
        <input type="text" value={currInput} onKeyPress={this.handleKey} />
        <div>{currInput}</div>
        <div>{currInput}</div>
        <div>{currInput}</div>
        <div>{currInput}</div>
        <div>{currInput}</div>
        // a 1000 div use currInput        <div>{currInput}</div>
    </div>
);
Here the beauty is that we have to write a 1000 lines of <div> to clearly say that another 1000 controls will be affected.

Why the other framework's two-way binding is bad?

<head>
    <title>Observable 2</title>
    <script src="Scripts/jquery-1.11.0.min.js"></script>
    <script src="Scripts/knockout-3.2.0.js"></script>
    <script>
        function myViewModel1() {
            this.myData1 = ko.observable('hello world');
        }
 
        $(function () {
            ko.applyBindings(new myViewModel1());
        });
    </script>
 
</head>
<body>
    <input data-bind="value: myData1, valueUpdate: 'keyup'" />
    <input data-bind="value: myData1, valueUpdate: 'keyup'" />
    <input data-bind="value: myData1, valueUpdate: 'keyup'" />
    <input data-bind="value: myData1, valueUpdate: 'keyup'" />
    // could have 1000 input anywhere in the page
    <input data-bind="value: myData1, valueUpdate: 'keyup'" />
 
    <div class="myDiv" data-bind="text: myData1"></div>
    <div class="myDiv" data-bind="text: myData1"></div>
    <div class="myDiv" data-bind="text: myData1"></div>
    <div class="myDiv" data-bind="text: myData1"></div>
    <div class="myDiv" data-bind="text: myData1"></div>
    // could have 1000 div anywhere in the page
    <div class="myDiv" data-bind="text: myData1"></div>
</body>

As you can see the two-way data binding of Knockout make things are so simple, only a few lines of JavaScript codes are needed to bind a few thousands of controls all together to a single data.

Any input can take a change and the change is made automatically to the other few thousands controls.

This is bad because in a large app, it really hard to keep track where are all these thousands of controls and data flow is a mess.

The power of two-way binding.

With this sample, you can feel the power/mess of two-way data bind of knockout. Typing in any of the input box will change anything else on the page. Imagine this a large single page application with controls everywhere.



http://facebook.github.io/react/docs/two-way-binding-helpers.html
http://n12v.com/2-way-data-binding/

No comments:

Post a Comment