コンポーネントの種類に基づいてコンポーネントを動的にレンダリングしようとしています。
例えば:
var type = "Example";
var ComponentName = type + "Component";
return <ComponentName />;
// Returns <examplecomponent /> instead of <ExampleComponent />
ここで提案した解決策を試してみました React/JSX動的コンポーネント名
コンパイル時にエラーが発生しました(gulpにbrowserifyを使用)。配列構文を使用している場所ではXMLが必要でした。
すべてのコンポーネントに対してメソッドを作成することでこれを解決できます。
newExampleComponent() {
return <ExampleComponent />;
}
newComponent(type) {
return this["new" + type + "Component"]();
}
しかし、それは私が作成するすべてのコンポーネントに対して新しい方法を意味するでしょう。この問題に対してもっと洗練された解決策がなければなりません。
私は提案に非常にオープンです。
<MyComponent />
はReact.createElement(MyComponent, {})
にコンパイルされます。これは文字列(HTMLタグ)または関数(ReactClass)を最初のパラメータとして期待します。
コンポーネントクラスを大文字で始まる名前の変数に格納するだけです。 HTMLタグとReactコンポーネント を参照してください。
var MyComponent = Components[type + "Component"];
return <MyComponent />;
にコンパイル
var MyComponent = Components[type + "Component"];
return React.createElement(MyComponent, {});
そのような状況に対処する方法についての公式文書はここにあります: https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-実行時タイプ
基本的にそれは言う:
違う:
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Wrong! JSX type can't be an expression.
return <components[props.storyType] story={props.story} />;
}
正しい:
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
私は新しい解決策を考え出しました。私はES6モジュールを使用しているので、クラスを必要としていることに注意してください。代わりに新しいReactクラスを定義することもできます。
var components = {
example: React.createFactory( require('./ExampleComponent') )
};
var type = "example";
newComponent() {
return components[type]({ attribute: "value" });
}
あなたのコンポーネントがグローバルであるならば、あなたは単にすることができます:
var nameOfComponent = "SomeComponent";
React.createElement(window[nameOfComponent], {});
ラッパーコンポーネントの場合、単純な解決策は単にReact.createElement
を直接使用することです(ES6を使用)。
import RaisedButton from 'mui/RaisedButton'
import FlatButton from 'mui/FlatButton'
import IconButton from 'mui/IconButton'
class Button extends React.Component {
render() {
const { type, ...props } = this.props
let button = null
switch (type) {
case 'flat': button = FlatButton
break
case 'icon': button = IconButton
break
default: button = RaisedButton
break
}
return (
React.createElement(button, { ...props, disableTouchRipple: true, disableFocusRipple: true })
)
}
}
動的に使用されることになっているすべてのコンポーネントにコンポーネント名をマッピングするコンテナがあるはずです。モジュール環境では、コンポーネントクラスにアクセスできる場所が1つもないため、コンポーネントクラスはコンテナに登録する必要があります。関数name
は本番環境では縮小されているため、コンポーネントクラスを明示的に指定しないと名前で識別できません。
それは普通のオブジェクトにすることができます。
class Foo extends React.Component { ... }
...
const componentsMap = { Foo, Bar };
...
const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;
またはMap
instance:
const componentsMap = new Map([[Foo, Foo], [Bar, Bar]]);
...
const DynamicComponent = componentsMap.get(componentName);
平易なオブジェクトは、プロパティの短縮形から恩恵を受けるため、より適しています。
名前付きエクスポート付きの バレルモジュール は、そのようなマップとして機能します。
// Foo.js
export class Foo extends React.Component { ... }
// dynamic-components.js
export * from './Foo';
export * from './Bar';
// some module that uses dynamic component
import * as componentsMap from './dynamic-components';
const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;
これはモジュールコードスタイルごとに1つのクラスでうまく機能します。
デコレータはシンタックスシュガーのクラスコンポーネントと一緒に使うことができます。これにはクラス名を明示的に指定してマップに登録する必要があります。
const componentsMap = {};
function dynamic(Component) {
if (!Component.displayName)
throw new Error('no name');
componentsMap[Component.displayName] = Component;
return Component;
}
...
@dynamic
class Foo extends React.Component {
static displayName = 'Foo'
...
}
デコレータは、機能的な要素を持つ高次の要素として使うことができます。
const Bar = props => ...;
Bar.displayName = 'Bar';
export default dynamic(Bar);
Randomプロパティの代わりに 非標準のdisplayName
を使用することもデバッグに役立ちます。
次のコードは、URLの検索文字列から解析された文字列を使用してこれを実現する方法の実用的な例を示しています。
これらのURLパスを使用して、2つの固有のビューを持つページ「snozberrys」にアクセスしたいとします。
'http://localhost:3000/snozberrys?aComponent'
そして
'http://localhost:3000/snozberrys?bComponent'
ビューのコントローラをこのように定義します。
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import {
BrowserRouter as Router,
Route
} from 'react-router-dom'
import AComponent from './AComponent.js';
import CoBComponent sole from './BComponent.js';
const views = {
aComponent: <AComponent />,
console: <BComponent />
}
const View = (props) => {
let name = props.location.search.substr(1);
let view = views[name];
if(view == null) throw "View '" + name + "' is undefined";
return view;
}
class ViewManager extends Component {
render() {
return (
<Router>
<div>
<Route path='/' component={View}/>
</div>
</Router>
);
}
}
export default ViewManager
ReactDOM.render(<ViewManager />, document.getElementById('root'));
私達は私達の実際の部品を常に知っているので私はスイッチケースを適用することを考えたので、私は少し違うアプローチを使いました。私の場合、コンポーネントの総数も7〜8程度でした。
getSubComponent(name) {
let customProps = {
"prop1" :"",
"prop2":"",
"prop3":"",
"prop4":""
}
switch (name) {
case "Component1": return <Component1 {...this.props} {...customProps} />
case "Component2": return <Component2 {...this.props} {...customProps} />
case "component3": return <component3 {...this.props} {...customProps} />
}
}