【问题标题】:Create an instance of a React class from a string从字符串创建 React 类的实例
【发布时间】: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!

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

谢谢, 伊万

【问题讨论】:

    标签: class dynamic reactjs instance


    【解决方案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

          【讨论】:

            猜你喜欢
            • 2020-07-30
            • 1970-01-01
            • 1970-01-01
            • 2011-04-14
            • 2012-02-26
            • 1970-01-01
            • 1970-01-01
            • 2011-04-09
            相关资源
            最近更新 更多