从字符串创建React类的实例

问题描述 投票:17回答:6

我有一个字符串,其中包含类的名称(这来自一个json文件)。这个字符串告诉我的Template Class哪个布局/模板用于数据(也在json中)。问题是我的布局没有显示。

Home.jsx:

//a template or layout.
var Home = React.createClass({
  render () {
    return (
    <div>Home layout</div>
    )
  }
});

Template.jsx:

var Template = React.createClass({
  render: function() {
    var Tag = this.props.template; //this is the name of the class eg. 'Home'
    return (
        <Tag />
    );
  }
});

我没有得到任何错误,但我也没有看到布局/ Home Class。我检查了props.template,这会记录正确的信息。此外,我可以在DOM中看到home元素。但它看起来像这样:

<div id='template-holder>
    <home></home>
</div>

如果我将以下行更改为:

var Tag = Home;
//this works but it's not dynamic!

任何想法,我如何解决这个问题?我确定它要么是简单的修复,要么是我做了一些愚蠢的事情。帮助将不胜感激。如果已经有人问过这个道歉(我找不到)。

谢谢,Ewan

class dynamic reactjs instance
6个回答
16
投票

这不起作用:

var Home = React.createClass({ ... });

var Component = "Home";
React.render(<Component />, ...);

但是,这将:

var Home = React.createClass({ ... });

var Component = Home;
React.render(<Component />, ...);

所以你只需要找到一种方法来在字符串"Home"和组件类Home之间进行映射。一个简单的对象将作为基本注册表使用,如果需要更多功能,可以从那里构建。

var components = {
  "Home": Home,
  "Other": OtherComponent
};

var Component = components[this.props.template];

7
投票

如果您可以将所有组件放在一个模块中,那么无需手动将类映射到字典即可实现:

   import * as widgets from 'widgets';
   var Type = widgets[this.props.template];
   ...
   <Type />

通配符import语句已经是字典,代码的工作方式与上一个答案中的注册表类似。

实际上,我认为你可以使用一个额外的映射来处理多个模块:

import * as widgets from 'widgets';
import * as widgets2 from 'widgets2';

const registry = Object.assign({}, widgets, widgets2);
const widget = registry[this.props.template];

我完全会这样做。事实上我认为我是。


2
投票

我遇到了同样的问题,并自己找到了解决方案。我不知道是否是“最好的实践”,但它有效,我现在在我的解决方案中使用它。

您可以简单地使用“邪恶”eval函数来动态创建反应组件的实例。就像是:

function createComponent(componentName, props, children){
  var component = React.createElement(eval(componentName), props, children);
  return component;
}

然后,只需将其调用到您想要的位置:

var homeComponent = createComponent('Home', [props], [...children]);

如果它符合您的需求,也许您可​​以考虑这样的事情。

希望能帮助到你。


2
投票

正如其他人所建议的那样,以下是字符串内容的工作方式,而无需将组件作为静态链接代码嵌入到包中。

import React from 'react';
import { Button } from 'semantic-ui-react';
import createReactClass from 'create-react-class';

export default class Demo extends React.Component {
    render() {
        const s = "return { render() { return rce('div', null, rce(components['Button'], {content: this.props.propA}), rce(components['Button'], {content: 'hardcoded content'})); } }"
        const createComponentSpec = new Function("rce", "components", s);
        const componentSpec = createComponentSpec(React.createElement, { "Button": Button });
        const component = React.createElement(createReactClass(componentSpec), { propA: "content from property" }, null);

        return (
            <div>
                {component}
            </div>
        )
    }
}

React类规范在字符串s中。请注意以下事项:

rce代表React.createElement,并在致电createComponentSpec时作为第一个参数。

components是一个额外组件类型的字典,在调用createComponentSpec时作为第二个参数给出。这样做是为了您可以提供具有冲突名称的组件。

例如,字符串Button可以解析为标准HTML按钮,或来自语义UI的按钮。

您可以使用s中描述的https://babeljs.io轻松生成https://reactjs.org/docs/react-without-jsx.html的内容。本质上,字符串不能包含JSX的东西,并且必须是纯JavaScript。这就是BabelJS通过将JSX转换为JavaScript所做的事情。

你需要做的就是用React.createElement替换rce,并通过components字典解析外部组件(如果你不使用外部组件,你可以跳过字典的东西)。

这与上面的代码相同。相同的<div>有两个语义UI Buttons。

JSX render()代码:

function render() {
  return (
    <div>
      <Button content={this.props.propA}/>
      <Button content='hardcoded content'/>
    </div>
  );
}

BabelJS将其翻译成:

function render() {
  return React.createElement("div", null, React.createElement(Button, {
    content: this.props.propA
  }), React.createElement(Button, {
    content: "hardcoded content"
  }));
}

你做了如上所述的替换:

render() { return rce('div', null, rce(components['Button'], {content: this.props.propA}), rce(components['Button'], {content: 'hardcoded content'})); }

调用createComponentSpec函数将为React类创建一个规范。

然后使用createReactClass将其转换为实际的React类。

然后与React.createElement一起生活。

您需要做的就是从主要组件render func返回它。


1
投票

使用JSX时,您可以呈现HTML标记(字符串)或React组件(类)。

当你执行var Tag = Home时,它可以工作,因为JSX编译器将它转换为:

var Template = React.createElement(Tag, {});

变量Tag在同一范围内,并且是React类。

    var Tag = Home = React.createClass({
                       render () {
                         return (
                         <div>Home layout</div>
                         )
                       }
                     });

当你这样做

var Tag = this.props.template; // example: Tag = "aClassName"

你在干嘛

var Template = React.createElement("aClassName", null);

但“aClassName”不是有效的HTML标记。

看看here


0
投票

我想知道如何从数据库加载的JSON规范中动态创建React类,所以我做了一些实验并想出来了。我的基本想法是我想通过GUI定义一个React应用程序,而不是在文本编辑器中输入代码。

这与React 16.3.2兼容。注意React.createClass已被移入自己的模块。

这是基本部分的精简版:

import React from 'react'
import ReactDOMServer from 'react-dom/server'
import createReactClass from 'create-react-class'

const spec = {
  // getDefaultProps
  // getInitialState
  // propTypes: { ... }
  render () {
    return React.createElement('div', null, 'Some text to render')
  }
}
const component = createReactClass(spec)
const factory = React.createFactory(component)
const instance = factory({ /* props */ })
const str = ReactDOMServer.renderToStaticMarkup(instance)
console.log(str)

您可以在此处查看更完整的示例:

https://github.com/brennancheung/02-dynamic-react/blob/master/src/commands/tests/createClass.test.js

© www.soinside.com 2019 - 2024. All rights reserved.