I've been working on trying to modularize my React.js app (that will be delivered as a Desktop app with Electron) in a way that if I make a new module in the future, I can just add a new folder and modify a couple of files and it should integrate fine. I got originally inspired by this article: https://www.nylas.com/blog/react-plugins/
After that point, I started doing as much research as I could and ended up creating a JSON file that would live in the server with a manifest of the plugins that are registered for that specific client. Something like this:
{
"plugins": [
{
"name": "Test Plugin",
"version": "0.0.1",
"path": "testplugin",
"file": "test",
"component":"TestPlugin"
},
{
"name": "Another Plugin",
"version": "0.0.1",
"path": "anothertest",
"file": "othertest",
"component":"TestPluginDeux"
}
]
}
After that, I made a couple folders that match the path
value and that contain a component that matches the name in the manifest (e.g. testplugin/test.jsx
that exports the TestPlugin
component as a default). I also made a pluginStore
file that reads the manifest and mounts the plugins in the this.state
.
Then, did a ton of research on Google and here and found this answer: React - Dynamically Import Components
With that function, I was able to iterate through the manifest, find the folders in the directory, and mount the plugins in the this.state
by running the mountPlugins()
function I had created in the pluginStore
, inside a componentDidMount()
method in my homepage.
So far so good. I'm using React-Router and I was able to mount the plugins dynamically in the State and able to load them in my Home Route by just calling them like this: <TestPlugin />
.
The issue that I have now, is that I wanted to dynamically create Routes that would load these components from the state, either by using the component
or the render
method, but I had no luck. I would always get the same result... Apparently I was passing an object instead of a String.
This was my last iteration at this attempt:
{this.state.modules.registered.map((item) =>
<Route exact path={`/${item.path}`} render={function() {
return <item.component />
}}></Route>
)}
After that, I made a Route that calls a PluginShell
component that is called by a Navlink
that sends the name of the plugin to inject and load it dynamically.
<Route exact path='/ex/:component' component={PluginShell}></Route>
But I ended having the same exact issue. I'm passing an object
and the createElement
function expected a string
.
I searched all over StackOverflow and found many similar questions with answers. I tried applying all the possible solutions with no luck.
EDIT: I have put together a GitHub repo that has the minimal set of files to reproduce the issue.
Here's the link: https://codesandbox.io/embed/aged-moon-nrrjc
import()
? It returns a promise that resolves to an object. If you are exporting usingexport default MyComponent
it will resolve to an object{ default: MyComponent }
. – Esophagusthis.state
. The components are exported as default. – MildpluginStore.getAll().registered.plugins
is returning. It might be possible that it is not returning a valid component. – Esophagus