从字符串创建 React 类的实例

Posted

技术标签:

【中文标题】从字符串创建 React 类的实例【英文标题】:Create an instance of a React class from a string 【发布时间】:2015-07-22 05:43:36 【问题描述】:

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

Home.jsx:

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

模板.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!

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

谢谢, 伊万

【问题讨论】:

【参考方案1】:

这不起作用:

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

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

但是,这会:

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

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

所以你只需要找到一种方法在 string "Home"component class Home 之间进行映射。一个简单的对象将用作基本注册表,如果您需要更多功能,您可以从那里构建。

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

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

【讨论】:

谢谢,我希望避免在代码中出现地图/链接。我希望能够在不更改主代码库的情况下添加模板。这不可能吗? @ewan 您可以构建某种全局注册表并在使用React.createClass 创建每个组件时注册它(例如,通过将其包装在函数调用或其他东西中),但您肯定需要获取对实际组件的引用。 太棒了@MichelleTilley!我的 linter 甚至简化为 var components = Home, Other, ; 并且运行良好。【参考方案2】:

无需像 Michelle 的回答那样手动将您的课程映射到字典或“注册表”。通配符导入语句已经是字典了!

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

您可以通过将所有字典合并为一个来使其与多个模块一起使用:

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

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

我完全会这样做以获得反应组件的动态调度。事实上,我认为我参与了很多项目。

【讨论】:

【参考方案3】:

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

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

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

然后,在你想要的地方调用它:

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

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

希望对你有帮助。

【讨论】:

我不会认为eval 是邪恶的,除非用户可以输入他们想要的任何内容。【参考方案4】:

我想知道如何根据从数据库加载的 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

【讨论】:

【参考方案5】:

这是它从字符串内容工作的方式,无需像其他人建议的那样将组件作为静态链接代码嵌入到包中。

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 中的按钮。

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

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

这里和上面的代码是等价的。相同的 &lt;div&gt; 有两个语义 UI Buttons。

JSX 渲染()代码:

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 返回它。

【讨论】:

【参考方案6】:

当您使用 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

【讨论】:

以上是关于从字符串创建 React 类的实例的主要内容,如果未能解决你的问题,请参考以下文章

java中如何以给定的字符串为名字创建某个类的实例?

python 高级教程:面向对象。

[Java开发之路]File类的使用

React的PropTYpes

Java File类的学习

C# 在类的新实例中使用 Forms 元素