Подтвердить что ты не робот

Создайте экземпляр класса 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

4b9b3361

Ответ 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.