Remote AJAX components for ReactJS

    Here we will talk about how, separately from the entire react application, to load the remote react component and render it! I will show how I solved this problem, because a year later, I can’t find similar solutions except react-remote-component-demo .



    When developing a project on React , the task was set, it is necessary that a single-page application on React load additional components on AJAX and show them, an additional difficulty was that these components must be corrected on the server regardless of the application itself.


    The structure of the application is simplified as follows, there is a list of components on the left, when I click on one of them, I load the remote component through AJAX and display its detailed view.


    Because I used Webpack when building , the very first attempts to google something led to the use of require.ensure .


    This turned out to be impossible in my case, because at the time of compiling the webpack, I don’t know how many remote components I will have, all I know is that let’s say the components will be distributed as static from a certain folder or the server will distribute them from the database.


    Accordingly, it turned out to be impossible to use CommonsChunkPlugin to tell the webpack, such and such input files should be put separately there, because I do not know how many files will be.


    In total, the react application itself is assembled using a webpack, and the remote components are prepared separately (removed in this case from the react application itself, so I gave them such a definition).


    I also wanted to write remote components beautifully on ES6. So it was necessary to use Babel in addition to compile my remote components.


    Through trial and error, I was able to get my remote component compiled.


    The component looked like this:


    class CMP extends React.Component {
      constructor(props) {
        super(props);
      }
      render() {
        return 
    Hello from FIRST remote component!
    {this.props.now}
    } } module.exports = CMP;

    Please note that this is a listing of the entire source remote component, there are no module loads from import ... from ...or to , otherwise it won’t work. Additionally at the end stands .... = require(...)node_modulesmodule.exports


    Here is such a component on ES6 1.jsxI can compile in ES5 1.jswith the help of a bubble so that it does not swear.


    After compilation, I have a finished text file with the component removed 1.js:


    1.js
    "use strict";
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
    var CMP = function (_React$Component) {
      _inherits(CMP, _React$Component);
      function CMP(props) {
        _classCallCheck(this, CMP);
        return _possibleConstructorReturn(this, (CMP.__proto__ || Object.getPrototypeOf(CMP)).call(this, props));
      }
      _createClass(CMP, [{
        key: "render",
        value: function render() {
          return React.createElement(
            "div",
            null,
            React.createElement(
              "div",
              null,
              "Hello from ",
              React.createElement(
                "strong",
                null,
                "FIRST"
              ),
              " remote component!"
            ),
            React.createElement(
              "div",
              null,
              this.props.now
            )
          );
        }
      }]);
      return CMP;
    }(React.Component);
    module.exports = CMP;

    This file can already be given statically or from the database.


    It remains to load this file into the react application, make a component out of it, and render it.


    The component that will do this is called Remote. And to display the list, we’ll call it List.
    The logic is something like this, it Listlistens to the user’s event click, determines which list item was clicked, and accordingly componentI pass this property Remoteto as props.
    Inside, RemoteI used the componentWillReceiveProps () function. So I determined that the property has changed and I need to render a detailed view of the transferred remote component. To do this, I check if it is in the component cache, and if not, I load it.


    It is not difficult to load our remote component (I use a higher-level wrapper over XMLHttpRequest for clarity).
    All magic happens next:


          ajax.load('/remote/' + requiredComponent)
            .then((str_component) => {
              let component;
              try {
                let
                  module = {},
                  Template = new Function('module', 'React', str_component);
                Template(module, React);
                component = React.createFactory(module.exports);
              } catch (e) {
                console.error(e);
              }
            })

    From the loaded line / component, I make a template function new Function(). As input parameters we define two string variables moduleand React. I now "call" this template Template(module, React).


    I create a new object module = {}and pass it to the first place, and to the second place I pass the react module.


    Thus, if you recall what we wrote inside the remote component:


    ... extends React ...

    and


    module.exports = ...

    When we "call" our function / template, we pass these two variables, there should be no error since we defined these two variables.


    As a result, the result in the property is module = {}assigned to our object exports. So we solve two problems, bypass errors at the component compilation stage, using module.exportsand React. And having defined them as input parameters, we “execute” our template already in the browser.


    It remains to create our component component = React.createFactory(module.exports)and render it:


      render() {
        let component = ...;
        return 
    {component ? component({ now: Date.now() }) : null}
    }

    When calling our component, you can pass any parameters component({ now: Date.now() })that will be visible as props.


    Our remote component works like a native!


    The code of the requested application was posted on the react -remote-component github :
    To start, do the following:


    npm install install all modules


    npm run build-cmp compile our remote components in dist/remote


    npm run server-dev we start the webpack dev server, which will collect the entire application into RAM and distribute from there, and it will distribute the removed components as static.


    We go on http: // localhost: 8099 / in the browser.


    Also popular now: