简体   繁体   English

如何使用标签动态创建组件?

[英]How to dynamically create components with Tags?

I want to dynamically create JSX tags for imported components. 我想为导入的组件动态创建JSX标签。 So my idea is having something like this: 所以我的想法是这样的:

import DemoComponent from './DemoComponent';

class DynamicRendering extends Component {

  assembleResult() {
    const {
      democomponent
    } = this.props;

    const result = [];

    if (democomponent) {
      const Tag = `DemoComponent`;
      result.push(<Tag />);
    }

    return result;
  }

  render() {
    const result = this.assembleResult();
    return result;
  }
}

The idea is that I can pass a couple of different props to the component and then the component dynamically crates JSX tags and assembles them together. 我的想法是,我可以将几个不同的道具传递给组件,然后该组件动态创建JSX标签并将它们组装在一起。 The reason I want this because I have about 15 components I want to render dynamically. 我之所以想要这个,是因为我有大约15个要动态渲染的组件。 Instead of implicitly writing them all I would prefer to make a loop over them and create them dynamically if needed. 与其隐式地编写它们,不如我希望对其进行循环并根据需要动态创建它们。 That way I can keep this component DRY. 这样,我可以使该组件保持干燥。

The problem with the code above is that if you create a Tag like this, it will take it as a HTML element. 上面的代码的问题在于,如果您创建这样的Tag,它将把它作为HTML元素。 This causes an error because there are no such HTML elements like 'DemoComponent'. 这会导致错误,因为没有诸如“ DemoComponent”之类的HTML元素。 I managed to solve this problem by creating a mapping of the name of the props to the component which should get loaded. 我设法通过创建道具名称到应该加载的组件的映射来解决此问题。 See example below: 请参见下面的示例:

import DemoComponent from './DemoComponent';

const PROP_MODULE_MAP = new Map([
  ['democomponent', DemoComponent]
]);

class DynamicRendering extends Component {

  assembleResult() {
    const {
      democomponent
    } = this.props;

    const result = [];

    if (democomponent) {
      const Tag = PROP_MODULE_MAP.get('democomponent');
      result.push(<Tag />);
    }

    return result;
  }

  render() {
    const result = this.assembleResult();
    return result;
  }
}

But I was wondering if there was a simpler way then creating this Map. 但是我想知道是否有比创建此Map更简单的方法。 Is there another way how you can dynamically create JSX tags which represent a imported component? 还有另一种方法可以动态创建代表导入组件的JSX标签吗?

You can just let the parent pass the desired component type: 您可以让父级传递所需的组件类型:

Parent.js : Parent.js

import SomeComponent from './someComponent';
import Child from './child';

// the parent renders Child and passes the type SomeComponent as a prop
const Parent = () => <Child Elem={SomeComponent} />

Child.js : Child.js

// the Child renders the component type passed
// note that the prop "Elem" is capitalized so that it will not be treated as a html node
const Child = ({Elem}) => <Elem />;

export default Child;

This way the Child component is capable of rendering any component type that it gets passed. 通过这种方式, Child组件能够呈现其传递的任何组件类型。 This is much more flexible and does not require the Child to know all the components it should render at compile time. 这更加灵活,不需要Child在编译时知道它应该呈现的所有组件。

Note that when rendering the passed component type in the child the variable to render has to be capitalized or it will be treated as a usual html node. 请注意,在子级中渲染传递的组件类型时,要渲染的变量必须大写,否则将被视为普通的html节点。 See User-Defined Components Must Be Capitalized for details. 有关详细信息,请参见用户定义的组件必须大写

If you do not want the prop name to be capitalized you can reassign the value to a capitalized name in the child before rendering it: 如果您不希望将道具名称大写,则可以在渲染子代之前将值重新分配给子代中的大写名称:

const Child = ({elem: Elem}) => <Elem />;    

I don't know what is your project layout, but I suggest you could do something like this: 我不知道您的项目布局是什么,但我建议您可以执行以下操作:
create a file for dynamic component import in your components folder: 在您的components文件夹中创建一个用于动态组件导入的文件:

import Comp1 from './Comp1'
import Comp2 from './Comp2'

export default { Comp1, Comp2 }

create a helper for function for dynamic rendering: 为动态渲染功能创建一个助手:

import components from './components/dynamic'

const renderComponents = compNames => {
    // take compNames as a list
    const compsToRender = compNames.map(name => components[name])
    // get all components by compNames provided and return them in array
    return compsToRender.map(Component => <Component />)
}

Then call it where you want . 然后在需要的地方调用它。

<App>
    {renderComponents(['Comp1', 'Comp2'])}
</App>

If you want to pass props with component names, you could pass objects instead of strings and pass those in components inside the function, but I don't see why it will be better then just use plain components with props 如果您想传递带有组件名称的道具,则可以传递对象而不是字符串,并在函数内部的组件中传递它们,但是我不明白为什么只使用带有道具的普通组件会更好

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

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