简体   繁体   English

React - 动态导入组件

[英]React - Dynamically Import Components

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?有没有办法让文件只存储组件的名称和路径,然后将它们动态导入到另一个文件中?

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):单独的文件(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):主文件(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.这种方法允许我非常快速地添加/删除组件,因为导入在一个文件中并且一次只需要更改一行。

Since the question is really old, the answers maybe were ok.由于问题真的很老,所以答案可能还可以。 But nowadays, if someone have the same problem should use dynamic import, in order to load only the component needed and avoid to load all the different ones.但是现在,如果有人遇到同样的问题应该使用动态导入,以便只加载需要的组件并避免加载所有不同的组件。

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

try the example here: demo试试这里的例子: 演示

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检查这个

Here is another solution: We get the list of needed components list = ['c1', 'c2', 'c3'] .这是另一个解决方案:我们得到所需组件的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()).它可以从 json 文件拉到一个数组(我使用 redux-store 所以我开始通过 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.在情况下,我们需要从最近的服务器阿贾克斯它们,所以我们需要与要求(或类似的东西),不知道到底绑定前的模块分割。

You can bundle your components as micro-apps and hot load them into your application from a url.您可以将您的组件捆绑为微应用程序,并从 url 将它们热加载到您的应用程序中。 Here is a poc that supports dynamically importing components and micro-apps from a route based on a configuration on the site level.这是一个支持从基于站点级别配置的路由动态导入组件和微应用程序的 poc。

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

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诀窍是 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
};

and you may import it as usual class names:您可以将其作为通常的类名导入:

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

you could create a component building function that utilizes React.createElement .您可以创建一个使用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>
    )
}

} }

if we need to use dynamic import, in order to load only the component needed and avoid to load all the different ones.如果我们需要使用动态导入,为了只加载需要的组件,避免加载所有不同的组件。 using code-splitting使用代码分割

(async () => {
  const { Component1 } = await import('./Component1');
})();

You can use Route and Switch from 'react-router-dom' to dynamically render components based on the path.您可以使用 'react-router-dom' 中的 Route 和 Switch 来根据路径动态渲染组件。 Here is the sample这是示例

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM