Создайте экземпляр класса React из строки
У меня есть строка, которая содержит имя класса (это происходит из json файла). Эта строка сообщает моему классу шаблонов, какой макет/шаблон использовать для данных (также в json). Проблема заключается в том, что мой макет не отображается.
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 />
);
}
});
Я не получаю никаких ошибок, но я также не вижу макет/домашний класс. Я проверил файл props.template, и это ведет к правильной информации. Кроме того, я могу видеть домашний элемент в DOM. Однако это выглядит так:
<div id='template-holder>
<home></home>
</div>
Если я изменил следующую строку на:
var Tag = Home;
//this works but it not dynamic!
Любые идеи, как я могу это исправить? Я уверен, что это либо простое исправление, либо я делаю что-то глупое. Помощь была бы оценена. Извините, если это уже было задано (я не мог найти его).
Спасибо,
Ewan
Ответы
Ответ 1
Это не сработает:
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];
Ответ 2
Если вы можете жить со всеми вашими компонентами в одном модуле, то это работает без необходимости вручную сопоставлять ваши классы со словарем:
import * as widgets from 'widgets';
var 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]);
Если это соответствует вашим потребностям, возможно, вы можете рассмотреть что-то вроде этого.
Надеюсь, что это поможет.
Ответ 4
Когда вы используете JSX, вы можете либо визуализировать теги HTML (строки), либо компоненты React (классы).
Когда вы выполняете var Tag = Home, это работает, потому что компилятор JSX преобразует его в:
var Template = React.createElement(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.
Посмотрите здесь
Ответ 5
Я хотел знать, как динамически создавать классы React из спецификации JSON, загруженной из базы данных, и поэтому я провел некоторые эксперименты и выяснил это. Моя основная идея состояла в том, что я хотел определить приложение 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
Ответ 6
Вот как это будет работать со строковым содержимым без встраивания ваших компонентов в виде статически связанного кода в ваш пакет, как предлагали другие.
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 или кнопку из семантического интерфейса.
Вы можете легко создавать контент для s
, используя https://babeljs.io, как описано в https://reactjs.org/docs/react-without-jsx.html. По сути, строка не может содержать материал JSX и должна быть простым JavaScript. Это то, что BabelJS делает, переводя JSX на JavaScript.
Все, что вам нужно сделать, это заменить React.createElement
на rce
и разрешить внешние компоненты через словарь components
(если вы не используете внешние компоненты, то вы можете пропустить материал словаря).
Здесь эквивалентно тому, что в коде выше. Же <div>
с два семантическими UI Button
в нем.
Код JSX render():
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
в жизнь с помощью React.createElement
.
Все, что вам нужно сделать, это вернуть его из основного компонента render
func.