React - Dynamically Import Components

时间:2018-01-15 18:10:20

标签: javascript reactjs import components react-component

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:

    import React, { Component } from 'react'
    import Component1 from './Component1'
    import Component2 from './Component2'
    import Component3 from './Component3'

    class Main extends Component {
        render() {
            var components = {
                'Component1': Component1,
                'Component2': Component2,
                'Component3': Component3
            };
            var type = 'Component1';  // just an example
            var MyComponent = Components[type];
            return <MyComponent />
        }
    }

    export default Main

However, I change/add components all the time. Is there a way to perhaps have a file which stores ONLY the names and paths of the components and these are then imported dynamically in another file?

8 个答案:

答案 0 :(得分:12)

我认为对于我想要实现的目标可能存在一些困惑。我设法解决了我遇到的问题,并在下面显示了我的代码,显示了我是如何解决它的。

单独文件(ComponentIndex.js):

    let Components = {};

    Components['Component1'] = require('./Component1').default;
    Components['Component2'] = require('./Component2').default;
    Components['Component3'] = require('./Component3').default;

    export default Components

主文件(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

此方法允许我非常快速地添加/删除组件,因为导入在一个文件中,并且只需要一次更改一行。

答案 1 :(得分:1)

您可以将组件捆绑为微型应用程序,然后从URL将它们热加载到应用程序中。这是一个Poc,它支持基于站点级别的配置从路由动态导入组件和微型应用程序。

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

您可以创建一个使用React.createElement的组件构建功能。这样您就可以从帮助文件中导入该函数。如果没有更多信息,很难在此示例中显示更多代码,但如果您的目标是从此组件中完全删除逻辑,则可以使用此文件中的状态帮助程序。

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>
    )
}

}

答案 4 :(得分:0)

这是另一种解决方案: 我们获得所需组件list = ['c1', 'c2', 'c3']的列表。可以将其从json文件拉到数组中(我使用redux-store,所以我开始通过this.props.getForms()获取表单)。但是您可以只手动创建和访问组件列表。

    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>
        )
    }

因此,在加载您的应用程序时,它会拉出所需的模块。

我想使用 promise 导入它们,但是模块已经可以实现了。

以防我们最近需要将它们从服务器中删除,因此我们需要先拆分模块再与 require (或类似的东西)捆绑在一起,否则将无法完全了解。

答案 5 :(得分:0)

由于问题确实很老,所以答案可能还可以。但是如今,如果有人遇到相同的问题,则应使用动态导入,以便仅加载所需的组件并避免加载所有不同的组件。

const component = React.lazy(() => import('./component.jsx'));

在此处尝试示例:demo

答案 6 :(得分:0)

您可以使用“ react-router-dom”中的“路由和切换”来基于路径动态渲染组件。这是示例

render() {
return (
  <>
    <Header  />
    <BrowserRouter>
      <Switch>
        <Route path="/abc" exact render={() => (<Abc />)}/>
        <Route path="/abcd" exact render={() => (<Abcd {...this.props} />)}/>
        <Route path="/xyz" exact render={() => (<Xyz />)}/>
        
      </Switch>
    </BrowserRouter>
    <Footer /></>
);  }

答案 7 :(得分:-1)

另一种无需任何承诺即可进行动态导入的方法:

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;
  };
}

诀窍是babel将类编译为另一个名称,例如 react__WEBPACK_IMPORTED_MODULE_1 ___ default ,因此要访问它,我们需要在一个对象中分配编译模块名称,这就是为什么有setClasses可以使用以下对象编译对象的原因参考

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
};

,您可以将其导入为常规的班级名称:

new (setClasses[`ColumnSet${n}Brick`])(parameters)