0

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.

1 Answer 1

3

You don't need @loadable/component for two reasons.

  1. You can accomplish your goal with dynamic imports
  2. '@loadable/component' returns a React Component object, not your function.

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

Sign up to request clarification or add additional context in comments.

10 Comments

thanks! I'm looking at this more closely, since I can't seem to get the message to print inside the componentDidMount
Yeah - there is a race condition. This does work, let me fix it. But I'm going to hide this answer until is works properly.
I updated my question with an attempt that I used based on your answer
That's odd...I'll take a look in a read IDE instead of the repl stuff.
OK, here is the entire solution that renders the layers into the view. Had to move the parsing back to componentDidMount() to over come the race condition. Note too, that I had to use template literal string for the variable component name. But this works as expected now. Sorry for the mix up.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.