从字符串创建 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
字典解析外部组件(如果你不使用外部组件,你可以跳过字典的东西)。
这里和上面的代码是等价的。相同的 <div>
有两个语义 UI Button
s。
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 类的实例的主要内容,如果未能解决你的问题,请参考以下文章