Create an instance of a React class from a string

2019-02-11 22:25发布

I have a string which contains a name of the Class (this is coming from a json file). This string tells my Template Class which layout / template to use for the data (also in json). The issue is my layout is not displaying.

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

I don't get any errors but I also don't see the layout / Home Class. I've checked the props.template and this logs the correct info. Also, I can see the home element in the DOM. However it looks like this:

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

If I change following line to:

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

Any ideas, how I can fix this? I'm sure it's either simple fix or I'm doing something stupid. Help would be appreciated. Apologies if this has already been asked (I couldn't find it).

Thanks, Ewan

5条回答
你好瞎i
2楼-- · 2019-02-11 22:59

When you use JSX you can either render HTML tags (strings) or React components (classes).

When you do var Tag = Home, it works because the JSX compiler transforms it to:

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

with the variable Tag in the same scope and being a React class.

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

When you do

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

you are doing

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

But "aClassName" is not a valid HTML tag.

Look here

查看更多
霸刀☆藐视天下
3楼-- · 2019-02-11 23:06

This will not work:

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

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

However, this will:

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

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

So you simply need to find a way to map between the string "Home" and the component class Home. A simple object will work as a basic registry, and you can build from there if you need more features.

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

var Component = components[this.props.template];
查看更多
太酷不给撩
4楼-- · 2019-02-11 23:13

If you can live with having all your components in one module, then this works without having to manually map your classes to a dictionary:

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

The wildcard import statement is already a dictionary and the code works like the registry in the previous answer.

Actually, I think you could work with multiple modules with one additional mapping:

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

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

I would totally do this. In fact I think I am.

查看更多
在下西门庆
5楼-- · 2019-02-11 23:16

I wanted to know how to create React classes dynamically from a JSON spec loaded from a database and so I did some experimenting and figured it out. My basic idea was that I wanted to define a React app through a GUI instead of typing in code in a text editor.

This is compatible with React 16.3.2. Note React.createClass has been moved into its own module.

Here's condensed version of the essential parts:

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)

You can see a more complete example here:

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

查看更多
唯我独甜
6楼-- · 2019-02-11 23:17

I had the same problem, and found out the solution by myself. I don't know if is the "best pratice" but it works and I'm using it currently in my solution.

You can simply make use of the "evil" eval function to dynamically create an instance of a react component. Something like:

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

Then, just call it where you want:

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

If it fits your needs, maybe you can consider something like this.

Hope it helps.

查看更多
登录 后发表回答