I have a JSON file with several filepaths to scripts that I want to be able to load dynamically into my React app, to build each component based on specifications that are in the metadata. Currently I have the metadata in my app as a Metadata
data object.
metadata.json:
{
"component1": { "script": "./createFirstLayer.js" },
"component2": { "script": "./createSecondLayer.js" }
}
Each script exports a function that I want to be able to use to construct the component. For troubleshooting purposes, it currently only returns a simple message.
function createFirstLayer(name) {
return name + " loaded!";
}
export default createFirstLayer;
I did some research and identified the @loadable/component package. Using this package as import loadable from "@loadable/component";
, I attempted to load my script into App.js like this:
async componentDidMount() {
Object.keys(Metadata).forEach(function(name) {
console.log(Metadata[name].script);
var createLayer = loadable(() => import(Metadata[name].script));
var message = createLayer(name);
console.log(message);
});
}
Everything I have tried throws the TypeError createLayer is not a function
. How can I get the function loaded?
I have also attempted the lazy method.
I have recreated a working demo of my problem here.
EDIT: I have tried to put this at the top of my app
const scripts = {};
Object.keys(Metadata).forEach(async function(name) {
import(Metadata[name].script).then((cb) => scripts[name] = cb);
});
This causes the TypeError Unhandled Rejection (Error): Cannot find module './createFirstLayer.js'. (anonymous function) src/components lazy /^.*$/ groupOptions: {} namespace object:66
I have also attempted
const scripts = {};
Object.keys(Metadata).forEach(async function(name) {
React.lazy(() => import(Metadata[name].script).then((cb) => scripts[name] = cb));
});
My goal is to be able to call the appropriate function to create particular layer, and match them up in the metadata.
You don't need @loadable/component
for two reasons.
To use dynamic imports simply parse your JSON the way you were, but push the call to the import's default function into state
. Then all you have to do is render the "layers" from within the state.
Like this:
import React, { Component } from "react";
import Metadata from "./metadata.json";
class App extends Component {
constructor(props) {
super(props);
this.state = { messages: [] };
}
async componentDidMount() {
Object.keys(Metadata).forEach(name=> import(`${Metadata[name].script}`).then(cb =>
this.setState((state, props) => ({ messages: [...state.messages, cb.default(cb.default.name)] }))));
}
render() {
return (
<div className="App">
{this.state.messages.map((m, idx) => (
<h1 key={idx}>{m}</h1>
))}
</div>
);
}
}
export default App;
Here is the working example