📜 ⬆️ ⬇️

Remote AJAX Components for ReactJS

Here we will talk about how to separately load the remote reactant component separately from the entire reactor application and render it! I will show how I solved this problem, because a year later, I still cannot find similar solutions other than react-remote-component-demo .



When developing a project for React , a task was set; it is necessary that a single-page application on React loads additional components on AJAX and shows them; the additional complexity was that these components should be managed 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 via AJAX and display its detailed view.


Since when building, I used a webpack , then the very first attempts to google something led to the use of require.ensure .


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


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


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


I also wanted to write remote components beautifully on ES6. So it was necessary to use Babel in addition to compile my deleted 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 <div> <div>Hello from <strong>FIRST</strong> remote component!</div> <div>{this.props.now}</div> </div> } } module.exports = CMP; 

Please note that this is a listing of the entire source component that was removed, there are no import module loadings import ... from ... or ... = require(...) from node_modules , otherwise it will not work. Additionally, at the end is the module.exports .


This is the component on ES6 1.jsx I can compile in ES5 1.js with the help of a Babel so that it does not curse.


After compilation, I have a ready-made text file with the 1.js component 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 statics or from a database.


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


The component that will do this is called the Remote . And to display the list, we call List .
The logic is approximately as props : List listens to the click event of the user, determines which element of the list has been clicked and, accordingly, such a component property I props to Remote as props .
Inside the Remote I 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. For this, I check if it is in the component cache, and if not, then I load it.


It’s easy to load our remote component (I use a higher level wrapper over XMLHttpRequest for clarity).
All the 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 string / component, I am making a new Function() template function. As input parameters we define two string variables module and React . I now "call" this template with Template(module, React) .


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


Thus, if we recall what we wrote inside the removed component:


 ... extends React ... 

and


 module.exports = ... 

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


As a result, in our module = {} object, assign the result to the exports property. So we solve two problems, bypassing errors at the component compilation stage, using module.exports and React . And by defining 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 <div> {component ? component({ now: Date.now() }) : null} </div> } 

When calling our component, you can pass any component({ now: Date.now() }) parameters, which 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 run, do the following:


npm install install all modules


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


npm run server-dev run the web server's devs, which will collect the entire application into RAM and distribute from there, and distribute the components as static.


Go to http: // localhost: 8099 / in the browser.


')

Source: https://habr.com/ru/post/319678/


All Articles