Dynamically loading React components
There is a HOC component that you can import to do this. Components are dynamically loaded as micro apps into your host application.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
window.React = React;
window.ReactDOM = ReactDOM;
ReactDOM.render(<App />, document.getElementById('root'));
// app.js
import React from 'react';
import ReactDOM from 'react-dom';
import MicroApp from '@schalltech/honeycomb-react-microapp';
const App = () => {
return (
<MicroApp
config={{
View: {
Name: 'redbox-demo',
Scope: 'beekeeper',
Version: 'latest'
}
}}
/>
);
});
export default App;
The components are not installed or known at design time. If you get creative, using this approach you could update your components without needing to redeploy your host application.
https://github.com/Schalltech/honeycomb-marketplace#using-micro-apps
It's an interesting problem I also faced some months ago for customer work, and I didn't see too many document approaches out there. What we did is:
Individual plugins will be separate Webpack projects, for which we provide either a template or a CLI tool that generates project templates.
In this project we define Webpack
externals
for shared vendor libraries already used in the core application: React, Redux, etc. This tells the plugin to not include those in the bundle but to grab them from a variable inwindow
we set in the core app. I know, sounds like sucks, but it's much better than having all plugins re-include 1000s of shared modules.Reusing this concept of
external
, the core app also provides some services via window object to plugins. Most important one is aPluginService.register()
method which your plugin must call when it's initialized. We're inverting control here: the plugin is responsible to say "hi I'm here, this is my main export (the Component if it's a UI plugin)" to the core application.The core application has a PluginCache class/module which simply holds a cache for loaded plugins (pluginId -> whatever the plugin exported, fn, class, whatever). If some code needs a plugin to render, it asks this cache for it. This has the benefit of allowing to return a
<Loading />
or<Error />
component when a plugin did not load correctly, and so on.For plugin loading, this PluginService/Manager loads the plugin configuration (which plugins should I load?) and then creates dynamically injected
script
tags to load each plugin bundle. When the bundle is finished, theregister
call described in step 3 will be called and your cache in step 4 will have the component.Instead of trying to load the plugin directly from your component, ask for it from the cache.
This is a very high level overview which is pretty much tied to our requirements back then (it was a dashboard-like application where users could add/remove panels on the fly, and all those widgets were implemented as plugins).
Depending on your case, you could even wrap the plugins with a <Provider store={ theCoreStore }
> so they have to access to Redux, or setup an event bus of some kind so that plugins can interact with each other... There is plenty of stuff to figure out ahead. :)
Good luck, hope it helped somehow!