从字符串创建 React 类的实例

IT技术 class dynamic reactjs instance
2021-03-25 20:49:00

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

主页.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 />
    );
  }
});

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

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

如果我将以下行更改为:

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

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

谢谢,伊万

6个回答

这将不起作用:

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];
太棒了@MichelleTilley!我的 linter 甚至简化为var components = { Home, Other, };,并且运行良好。
2021-05-26 20:49:00
@ewan您可以构建某种全局注册表并在创建时注册每个组件React.createClass(例如通过将其包装在函数调用或其他东西中),但您肯定需要获取对实际组件的引用。
2021-05-29 20:49:00
谢谢,我希望避免在代码中包含地图/链接。我希望能够在不更改主代码库的情况下添加模板。这不可能吗?
2021-06-08 20:49:00

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

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

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

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

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

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

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

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

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

然后,只需在您想要的地方调用它:

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

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

希望能帮助到你。

eval除非用户可以输入他们想要的任何内容,否则我不会认为是邪恶的。
2021-05-31 20:49:00

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

这与 React 16.3.2 兼容。NoteReact.createClass已移入其自己的module中。

这是基本部分的精简版:

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/测试/createClass.test.js

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

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 类规范在 string 中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.createElementrce,并通过components字典解析外部组件(如果您不使用外部组件,则可以跳过字典的内容)。

这相当于上面代码中的内容。里面<div>有两个 Semantic UIButton也是一样。

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 类创建规范。

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

然后用React.createElement.

您需要做的就是从主组件renderfunc返回它