[Turn] react isomorphism thought

Source: Internet
Author: User



React what attracts me is that the client-server isomorphism feature, the server-to-client reusable component, provides a brief introduction to this architectural idea.


For space reasons, this article does not introduce the REACT Foundation, so if you are not yet clear about the basic concepts of react's state/props/life cycle, it is recommended to first learn the relevant documentation

Client react


Let's review how react writes a component. For example, to make a table below:



You can write this: Create a table class first. Table.js


 
var React = require(‘react‘); var DOM = React.DOM; var table = DOM.table, tr = DOM.tr, td = DOM.td; module.exports = React.createClass({
    render: function () { return table({
                children: this.props.datas.map(function (data) { return tr(null,
                        td(null, data.name),
                        td(null, data.age),
                        td(null, data.gender)
                    );
                })
            });
    }
});

The

assumes that we have structured data for the table we want. datas.js :


// three lines of data, including name, age, gender
Module.exports = [
     {
         ‘name’: ‘foo‘,
         ‘age’: 23,
         ‘gender’: ‘male’
     },
     {
         ‘name’: ‘bar’,
         ‘age’: 25,
         ‘gender’: ‘female‘
     },
     {
         ‘name’: ‘alice’,
         ‘age’: 34,
         ‘gender’: ‘male’
     }
]; 

After the

has the table class and the corresponding data, you can call and render the table. Render-client.js


Var React = require(‘react‘);
Var ReactDOM = require(‘react-dom‘);

/ / table class
Var Table = require(‘./Table‘);
/ / table instance
Var table = React.createFactory(Table);
// data source
Var datas = require(‘./datas‘);

// The render method renders the react instance to the page https://facebook.github.io/react/docs/top-level-api.html#reactdom
ReactDOM.render(
     Table({datas: datas}),
     Document.body
); 


We package theReact Base library,table.js,datas.js,render-client.js, etc.Pack.js, referring to the page:


 
 
 
<!doctype html>
<html>
    <head>
        <title>react</title>
    </head>
    <body>
    </body>
    <script src="pack.js"></script>
</html>‘


This allows the page to render a table by data structure to


Here pack.js the specific packaging tools can be grunt/gulp/webpack/browerify, etc., packaging method is not here to repeat


The key point of this example is to use props to pass a one-way data stream. For example, by traversing the data from the ``props data``` to generate each row of data in the table:


this.props.datas.map...


Each change to a component, such as new data, invokes the Render method inside the component to change its DOM structure. In this example, when you give thedataspush new data, react automatically adds data rows to the table in the page.


Service-side react


The components created in the example above,Tablefor performance, SEO and other factors, we will consider the server directly generated HTML structure, so you can directly render the DOM on the browser side.



At this point, ourTablecomponents can be used both on the client and the server side.



Just unlike the browser sideReactDOM.render's render target using the specified component, it is rendered in the server, using the Reactdomserver module, which has two methods for generating HTML strings:


    • Rendertostring
    • Rendertostaticmarkup


The difference between these two methods, I would like to put in the back to explain, because it is related to the content of the following.



With these two methods, we create a file that runs on the server-side NODEJS environment so that it can generate the HTML structure of the table directly on the server.



render-server.js:


Var React = require(‘react‘);

// slightly different from client require(‘react-dom‘)
Var React = require(‘react‘);

// slightly different from client require(‘react-dom‘)
Var ReactDOMServer = require(‘react-dom/server‘);

/ / table class
Var Table = require(‘./Table‘);
/ / table instance
Var table = React.createFactory(Table);

Module.exports = function () {
     Return ReactDOMServer.renderToString(table(datas));
}; 


The code above uses the sameTablecomponent to generate the HTML structure that the browser can render directly, and we'll make a real page by changing the Nodejs's official Hello world.



server.js :


 
 
var makeTable = require(‘./render-server‘);

var http = require(‘http‘);

http.createServer(function (req, res) {
  res.writeHead(200, {‘Content-Type‘: ‘text/html‘});

  var table = makeTable();
  var html = ‘<!doctype html>\n              <html>                <head>                    <title>react server render</title>                </head>                <body>‘ +
                    table +
                ‘</body>              </html>‘;

  res.end(html);
}).listen(1337, "127.0.0.1");

console.log(‘Server running at http://127.0.0.1:1337/‘);


Atnode server.jsthis time to run can see, not practical JS, to achieve the same table effect, here I used the sameTable.js, complete the client and the service side of the isomorphism, a code, two use.



Here we find some data in the DOM of the table by looking at the HTML source of the page:



data-reactid/What aredata-react-checksumthey? Here also leave some suspense, and then explain.


Server-side + client rendering


The above example, by invoking the same react component on the server, achieves the same interface effect, but someone may be unhappy: it seems a little weak!



The above example has two obvious problems:


    • Datas.js data sources are dead and do not conform to most real production environments

    • Server-side generated HTML structure is sometimes not perfect, sometimes not with the help of JS is not. For example, when our table needs to poll the server's data interface, to achieve the table data and server synchronization, how to implement a component at both ends of the use.


To solve this problem, our table component needs to become more complex.


Data source


Assuming that our tabular data is synchronized with the server over a period of time, on the browser side, we have to use theajaxreact official to show us the direction of such requirements andcomponentDidMountpull the data through this life cycle approach.



componentDidMountmethod, I personally compare it to a "aftercare" method, that is, after react the basic HTML structure to the DOM, then through it to do some aftercare things, such as pull data to update the DOM and so on.



So we changed our ``Table component, removed the fake data data.js, called our packaged grab data method in componentDidMount```, and fetched the data once every three seconds and updated it to the page.



table.js:


 
var React = require(‘react‘);
var ReactDOM = require(‘react-dom‘);

var DOM = React.DOM;
var table = DOM.table, tr = DOM.tr, td = DOM.td;

var Data = require(‘./data‘);

module.exports = React.createClass({
    render: function () {
        return table({
                children: this.props.datas.map(function (data) {
                    return tr(null,
                        td(null, data.name),
                        td(null, data.age),
                        td(null, data.gender)
                    );
                })
            });
    },
    componentDidMount: function () {
        setInterval(function () {
            Data.fetch(‘http://datas.url.com‘).then(function (datas) {
                this.setProps({
                    datas: datas
                });
            });
        }, 3000)
    }
});

This assumes that we have encapsulated a method for pulling dataData.fetch, such asData.fetch = jQuery.ajax


In this step, we implemented the client's automatic update of the tabular data every 3 seconds. So the above table component can be reused directly to the server to achieve data pull, sorry, the answer is "no".



One of the wonders of react is that its components have a "life cycle" , which invokes different life-cycle methods during different phases of the component's life, such as asynchronous data updates, Dom destruction, and so on.



However, the service side of the situation is different, to the service side, it is to do is: to the database pull data, based on the data generated HTML-spit to the client. This is a fixed process, pull data and generate HTML process is not scrambled, there is no first to spit the content to the client, and then pull the data such an asynchronous process.



So,componentDidMountThis "aftercare" method, react when the server renders the component, it does not apply.



And I'm going to tell you thatcomponentDidMountthis method is never going to be executed on the server!



See here, you may want to, this step pit dad! For a long while, this thing can only be used in the client, well isomorphic!



Don't worry, pull the data, we need another way.



React can bestaticsdefined by the "static method", learn the object-oriented programming of the classmate, naturally understand thestaticsmeaning of the method, did not learn, pull out to play 30 large slabs.



Let's changeTablethe components and put the logic of pull dataData.fetchhere.



table.js:


Var React = require(‘react‘);

Var DOM = React.DOM;
Var table = DOM.table, tr = DOM.tr, td = DOM.td;

Var Data = require(‘./data‘);

Module.exports = React.createClass({
    Statics: {
        fetchData: function (callback) {
            Data.fetch().then(function (datas) {
                Callback.call(null, datas);
            });
        }
    },
    Render: function () {
        Return table({
                Children: this.props.datas.map(function (data) {
                    Return tr(null,
                        Td(null, data.name),
                        Td(null, data.age),
                        Td(null, data.gender)
                    );
                })
            });
    },
    componentDidMount: function () {
        setInterval(function () {

            // When the component calls the statics method internally, use this.constructor.xxx...
            this.constructor.fetchData(function (datas) {
                this.setProps({
                    Datas: datas
                });
            });
        }, 3000);
    }
}); 

Very important: The key to the table component's ability to reuse the Fetchdata method on both the client and server side is that itData.fetchmust have different implementations on both the client and the server! For example, when the client calls,Data.fetchis to initiate an AJAX request, and on the server callData.fetch, it may be through the UDP protocol from other data servers to obtain data, query the database and other implementation


Because the server react will not be calledcomponentDidMount, you need to change the server rendering of the file, also no longer through the datas.js to get the data, but instead call the static method of tablefetchData, get the data, and then passed to the service-side rendering methodrenderToString, Getting data is an asynchronous process in a real production environment, so our code needs to be asynchronous as well:



render-server.js:


Var React = require(‘react‘);
Var ReactDOMServer = require(‘react-dom/server‘);

/ / table class
Var Table = require(‘./Table‘);
/ / table instance
Var table = React.createFactory(Table);

Module.exports = function (callback) {
     Table.fetchData(function (datas) {
         Var html = ReactDOMServer.renderToString(table({datas: datas}));
         Callback.call(null, html);
     });
}; 


At this time, ourTablecomponents have been implemented every 3 seconds to update the data, so we need to call the react initial HTML data on the server, but also in the client call react Live Update, so we need to introduce our packaged JS in the page.



Server.js


 
var makeTable = require(‘./render-server‘); var http = require(‘http‘);

http.createServer(function (req, res) { if (req.url === ‘/‘) {
        res.writeHead(200, {‘Content-Type‘: ‘text/html‘});

        makeTable(function (table) { var html = ‘<!doctype html>\n                      <html>                        <head>                            <title>react server render</title>                        </head>                        <body>‘ +
                            table + ‘<script src="pack.js"></script>                        </body>                      </html>‘;

            res.end(html);
        });
    } else {
        res.statusCode = 404;
        res.end();
    }

}).listen(1337, "127.0.0.1"); console.log(‘Server running at http://127.0.0.1:1337/‘);
Results


Through the above changes, we get the table data on the server, generate HTML for the browser to render directly, after the page rendering, the table component every 3 seconds through Ajax to obtain new tabular data, with data updates, will be directly updated to the page DOM.


The role of checksum


Do you remember the previous question?



ReactDOMServer.renderToStringAndReactDOMServer.renderToStaticMarkupWhat's the difference? What did the server generatedata-react-checksum?



We think, even if the server does not initialize the HTML data, just rely on the client's react can also fully implement rendering our table, the server generated the HTML data, will not be re-rendered when the client react execution? Our service side painstakingly generated things, by the client ruthlessly covered?



Of course not! When react is rendered on the server, the corresponding checksum (checksum) is generated for the component, so that the client react is working on the same component by reusing the initial DOM generated by the server and incrementally updating itdata-react-checksum.



ReactDOMServer.renderToStringandReactDOMServer.renderToStaticMarkupthe difference at this time is good to explain that the former will generate checksum for the component, and the latter will not, the latter only generates HTML structure data.



Therefore, only you do not want to be used when the client-server operation of the same component at the same timerenderToStaticMarkup.



[Turn] react isomorphism thought


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.