web-dev-qa-db-ja.com

文字列から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でhome要素を確認できます。ただし、次のようになります。

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

次の行を次のように変更した場合:

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

任意のアイデア、これをどのように修正できますか?私はそれが単純な修正であるか、私が愚かなことをしていると確信しています。助けていただければ幸いです。これが既に尋ねられた場合は申し訳ありません(私はそれを見つけることができませんでした)。

ありがとう、ユアン

21
ewan

これは機能しません:

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

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

ただし、これは次のようになります。

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

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

したがって、string"Home"component classHomeをマッピングする方法を見つけるだけです。単純なオブジェクトは基本的なレジストリとして機能し、さらに機能が必要な場合はそこから構築できます。

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

var Component = components[this.props.template];
19
Michelle Tilley

すべてのコンポーネントを1つのモジュールに収めることができれば、クラスを手動で辞書にマッピングしなくても機能します。

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

ワイルドカードインポートステートメントは既に辞書であり、コードは前の回答のレジストリのように機能します。

実際、1つの追加マッピングで複数のモジュールを操作できると思います。

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

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

私はこれを完全に行います。実際私はそうだと思います。

8
Robert Moskal

同じ問題があり、自分で解決策を見つけました。 「ベストプラクティス」であるかどうかはわかりませんが、問題なく機能し、現在私のソリューションで使用しています。

"evil" eval関数を使用して、動的にコンポーネントのインスタンスを動的に作成できます。何かのようなもの:

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

次に、必要な場所に呼び出します。

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

それがあなたのニーズに合っているなら、おそらくあなたはこのようなものを考えることができます。

それが役に立てば幸い。

3
dgpedro

他の人が示唆したように、静的にリンクされたコードとしてコンポーネントをパッケージに埋め込むことなく、文字列コンテンツから機能する方法は次のとおりです。

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です。次の点に注意してください。

rceReact.createElementを表し、createComponentSpecを呼び出すときに最初のパラメーターとして指定されます。

componentsは追加のコンポーネントタイプのディクショナリであり、createComponentSpecを呼び出すときに2番目のパラメーターとして指定されます。これは、競合する名前をコンポーネントに提供できるようにするためです。

たとえば、文字列Buttonは、標準のHTMLボタン、またはセマンティックUIのボタンに解決できます。

https://reactjs.org/docs/react-without-jsx.html で説明されているように、 https://babeljs.io を使用してsのコンテンツを簡単に生成できます。基本的に、文字列はJSXのものを含むことができず、プレーンJavaScriptである必要があります。これが、BabelJSがJSXをJavaScriptに変換することで行っていることです。

React.createElementrceに置き換え、components辞書を介して外部コンポーネントを解決する必要があります(外部コンポーネントを使用しない場合は、辞書をスキップできます)。 。

上記のコードと同じです。同じ<div>に2つのセマンティックUI Buttonsが含まれています。

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で実現しました。

あなたがする必要があるのはメインコンポーネントrender funcからそれを返すことだけです。

2
zmechanic

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タグではありません。

見て ここ

1
gabrielgiussi

データベースからロードされた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

1
Brennan Cheung