React / JSX动态组件名称

问题描述 投票:117回答:9

我试图根据其类型动态呈现组件。

例如:

var type = "Example";
var ComponentName = type + "Component";
return <ComponentName />; 
// Returns <examplecomponent />  instead of <ExampleComponent />

我尝试了这里提出的解决方案React/JSX dynamic component names

这在编译时给了我一个错误(使用browserify for gulp)。它期望我使用数组语法的XML。

我可以通过为每个组件创建一个方法来解决这个问题:

newExampleComponent() {
    return <ExampleComponent />;
}

newComponent(type) {
    return this["new" + type + "Component"]();
}

但这对我创建的每个组件都意味着一种新方法。必须有一个更优雅的解决方案来解决这个问题。

我对建议很开放。

javascript reactjs react-jsx
9个回答
120
投票

<MyComponent />编译为React.createElement(MyComponent, {}),它需要一个字符串(HTML标记)或一个函数(ReactClass)作为第一个参数。

您可以将组件类存储在名称以大写字母开头的变量中。见HTML tags vs React Components

var MyComponent = Components[type + "Component"];
return <MyComponent />;

编译成

var MyComponent = Components[type + "Component"];
return React.createElement(MyComponent, {});

102
投票

这里有一个关于如何处理这种情况的官方文档:https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime

基本上它说:

错误:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
    photo: PhotoStory,
    video: VideoStory
};

function Story(props) {
    // Wrong! JSX type can't be an expression.
    return <components[props.storyType] story={props.story} />;
}

正确:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
    photo: PhotoStory,
    video: VideoStory
};

function Story(props) {
    // Correct! JSX type can be a capitalized variable.
    const SpecificStory = components[props.storyType];
    return <SpecificStory story={props.story} />;
}

10
投票

我想出了一个新的解决方案。请注意我使用的是ES6模块,所以我要求上课。您也可以定义一个新的React类。

var components = {
    example: React.createFactory( require('./ExampleComponent') )
};

var type = "example";

newComponent() {
    return components[type]({ attribute: "value" });
}

7
投票

如果您的组件是全局的,您可以简单地执行:

var nameOfComponent = "SomeComponent";
React.createElement(window[nameOfComponent], {});

6
投票

对于包装器组件,一个简单的解决方案是直接使用React.createElement(使用ES6)。

import RaisedButton from 'mui/RaisedButton'
import FlatButton from 'mui/FlatButton'
import IconButton from 'mui/IconButton'

class Button extends React.Component {
  render() {
    const { type, ...props } = this.props

    let button = null
    switch (type) {
      case 'flat': button = FlatButton
      break
      case 'icon': button = IconButton
      break
      default: button = RaisedButton
      break
    }

    return (
      React.createElement(button, { ...props, disableTouchRipple: true, disableFocusRipple: true })
    )
  }
}

4
投票

应该有一个容器将组件名称映射到应该动态使用的所有组件。组件类应该在容器中注册,因为在模块化环境中,没有一个地方可以访问它们。组件类无法通过其名称进行标识,而无需明确指定它们,因为函数name在生产中被缩小。

Component map

它可以是普通的对象:

class Foo extends React.Component { ... }
...
const componentsMap = { Foo, Bar };
...
const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;

或者Map实例:

const componentsMap = new Map([[Foo, Foo], [Bar, Bar]]);
...
const DynamicComponent = componentsMap.get(componentName);

普通物体更适合,因为它受益于物业速记。

Barrel module

具有命名导出的barrel module可以充当这样的映射:

// Foo.js
export class Foo extends React.Component { ... }

// dynamic-components.js
export * from './Foo';
export * from './Bar';

// some module that uses dynamic component
import * as componentsMap from './dynamic-components';

const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;

这适用于每个模块代码样式一个类。

Decorator

装饰器可以与类组件一起用于语法糖,这仍然需要显式指定类名并在映射中注册它们:

const componentsMap = {};

function dynamic(Component) {
  if (!Component.displayName)
    throw new Error('no name');

  componentsMap[Component.displayName] = Component;

  return Component;
}

...

@dynamic
class Foo extends React.Component {
  static displayName = 'Foo'
  ...
}

装饰器可用作具有功能组件的高阶组件:

const Bar = props => ...;
Bar.displayName = 'Bar';

export default dynamic(Bar);

使用non-standard displayName而不是随机属性也有利于调试。


0
投票

假设我们希望通过动态组件加载来访问各种视图。下面的代码给出了如何通过使用从URL的搜索字符串解析的字符串来实现此目的的工作示例。

假设我们想要使用这些url路径访问具有两个唯一视图的页面'snozberries':

'http://localhost:3000/snozberrys?aComponent'

'http://localhost:3000/snozberrys?bComponent'

我们像这样定义视图的控制器:

import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import {
  BrowserRouter as Router,
  Route
} from 'react-router-dom'
import AComponent from './AComponent.js';
import CoBComponent sole from './BComponent.js';

const views = {
  aComponent: <AComponent />,
  console: <BComponent />
}

const View = (props) => {
  let name = props.location.search.substr(1);
  let view = views[name];
  if(view == null) throw "View '" + name + "' is undefined";
  return view;
}

class ViewManager extends Component {
  render() {
    return (
      <Router>
        <div>
          <Route path='/' component={View}/>
        </div>
      </Router>
    );
  }
}

export default ViewManager

ReactDOM.render(<ViewManager />, document.getElementById('root'));

-1
投票

我使用了一些不同的方法,因为我们总是知道我们的实际组件,所以我想应用switch case。在我的案例中,组件的总数没有大约7-8。

getSubComponent(name) {
    let customProps = {
       "prop1" :"",
       "prop2":"",
       "prop3":"",
       "prop4":""
    }

    switch (name) {
      case "Component1": return <Component1 {...this.props} {...customProps} />
      case "Component2": return <Component2 {...this.props} {...customProps} />
      case "component3": return <component3 {...this.props} {...customProps} />

    }
  }

-1
投票

编辑:其他答案更好,请参阅评论。

我这样解决了同样的问题:

...
render : function () {
  var componentToRender = 'component1Name';
  var componentLookup = {
    component1Name : (<Component1 />),
    component2Name : (<Component2 />),
    ...
  };
  return (<div>
    {componentLookup[componentToRender]}
  </div>);
}
...
© www.soinside.com 2019 - 2024. All rights reserved.