React - Dynamically Import Components

前端 未结 8 1579
别那么骄傲
别那么骄傲 2020-12-05 07:14

I have a page which renders different components based on user input. At the moment, I have hard coded the imports for each component as shown below:

    imp         


        
相关标签:
8条回答
  • 2020-12-05 07:53

    One more way to make dynamic import without any promise:

    import React from "react";
    import ColumnSet1Brick from "./sets/column-set/column-set-1-brick";
    import ColumnSet2Brick from "./sets/column-set/column-set-2-brick";
    import ColumnSet3Brick from "./sets/column-set/column-set-3-brick";
    import ColumnSet4Brick from "./sets/column-set/column-set-4-brick";
    
    const setClasses = {
      ColumnSet1Brick,
      ColumnSet2Brick,
      ColumnSet3Brick,
      ColumnSet4Brick
    };
    
    export default class SetBrickStack extends React.Component {
    
      ...
    
    
    
      getColumnSetInstance = (n) => new (setClasses[`ColumnSet${n}Brick`])(this.paramFactory.getBasicProps());
    
      getBricksOnInit = () => {
        const columnSets = [1, 2, 3, 4];
        const bricksParams = columnSets.map(this.getColumnSetInstance);
        return bricksParams;
      };
    }
    

    the trick is that babel compiles the classes to another name such as react__WEBPACK_IMPORTED_MODULE_1___default so to access it we need to assign the compile module name in one object, that's why there is setClasses which compiles to object with references

    const setClasses = {
      ColumnSet1Brick: react__WEBPACK_IMPORTED_MODULE_1___default,
      ColumnSet2Brick: react__WEBPACK_IMPORTED_MODULE_2___default,
      ColumnSet3Brick: react__WEBPACK_IMPORTED_MODULE_3___default,
      ColumnSet4Brick: react__WEBPACK_IMPORTED_MODULE_4___default
    };
    

    and you may import it as usual class names:

    new (setClasses[`ColumnSet${n}Brick`])(parameters)
    
    0 讨论(0)
  • 2020-12-05 08:00

    I think there may have been some confusion as to what I was trying to achieve. I managed to solve the issue I was having and have shown my code below which shows how I solved it.

    Separate File (ComponentIndex.js):

        let Components = {};
    
        Components['Component1'] = require('./Component1').default;
        Components['Component2'] = require('./Component2').default;
        Components['Component3'] = require('./Component3').default;
    
        export default Components
    

    Main File (Main.js):

        import React, { Component } from 'react';
        import Components from './ComponentIndex';
    
        class Main extends Component {
            render () {
                var type = 'Component1'; // example variable - will change from user input
                const ComponentToRender = Components[type];
                return <ComponentToRender/>
            }
        }
    
        export default Main
    

    This method allows me to add/remove components very quickly as the imports are in one file and only requires changing one line at a time.

    0 讨论(0)
  • 2020-12-05 08:01
    import React, { Component } from 'react'
    import Component1 from './Component1'
    import Component2 from './Component2'
    import Component3 from './Component3'
    
    class Main extends Component {
        render() {
            var type = 'Component1';  // just an example
            return (
              <div>
                {type == "Component1" && <Component1 />}
                {type == "Component2" && <Component2 />}
                ...
              </div>
            )
        }
    }
    
    export default Main
    

    You can use conditional rendering insted. Hope it will help

    Check this

    0 讨论(0)
  • 2020-12-05 08:02

    Here is another solution: We get the list of needed components list = ['c1', 'c2', 'c3']. It may be pulled from json file to an array (i use redux-store so i initiate getting forms by this.props.getForms()). But you may just create and access the list of components manually.

        componentDidMount = () => {
    //we get elements list from any source to redux-store
            this.props.getForms();
    //access redux-store to the list
            const forms = this.props.configBody.sets;
    //make deep object copy
            const updatedState = { ...this.state };
            updatedState.modules = [];
            if (forms) {
    //here is the very dynamic import magic: we map the import list and prepare to store the imports in Component`s state
                const importPromises = forms.map(p =>
                    import(`../TemplateOrders/Template${p.order}`)
                        .then(module => {
                            updatedState.modules.push(module.default)
                        })
                        .catch(errorHandler(p))
                )
    //wait till all imports are getting resolved
                Promise.all(importPromises)
                    .then(res =>
    //then run setState
                        this.setState({ ...updatedState }, () => {
                            console.log(this.state);
                        }))
            }
        }
    
        render() {
            const forms = this.props.configBody.sets;
    //we iterate through the modules and React.createElemet`s 
            const list = this.state.modules
                ? this.state.modules.map((e, i) =>
                    createElement(e, { key: forms[i].title }, null)
                )
                : [];
            return (
                <Fragment>
                    <Link to='/'>Home</Link>
                    <h1>hello there</h1>
    //push them all to get rendered as Components
                    {list.map(e => e)}
                </Fragment>
            )
        }
    

    So when your app is loaded it pulls the needed modules.

    I thought to use promises to import them, but modules are already promises.

    In case we need to ajax them from server lately, so we need to split the modules before bundling with require (or something like that) dont know exactly.

    0 讨论(0)
  • 2020-12-05 08:03

    you could create a component building function that utilizes React.createElement. this way you can import the function from a helper file. hard to show more code in this example without more information, but you can use state helpers from this file too if your goal is to completely remove the logic from this component.

    class Main extends Component {
    
    constructor(props) {
      super();
      this.state = { displayComponent: Component1 }
    }
    
    buildComponent = () => {
      // create element takes additional params for props and children
      return React.createElement( this.state.displayComponent )
    }
    
    render() {
        var type = 'Component1';  // just an example
        return (
          <div>
            { this.buildComponent() }
          </div>
        )
    }
    

    }

    0 讨论(0)
  • 2020-12-05 08:05

    You can bundle your components as micro-apps and hot load them into your application from a url. Here is a poc that supports dynamically importing components and micro-apps from a route based on a configuration on the site level.

    https://github.com/eschall/react-micro-frontend

    0 讨论(0)
提交回复
热议问题