React / JSX Dynamic Component Name

I am trying to dynamically display components based on their type.

For example:

var type = "Example"; var ComponentName = type + "Component"; return <ComponentName />; // Returns <examplecomponent /> instead of <ExampleComponent /> 

I tried the solution proposed here Dynamic React / JSX Component Names

This gave me an error while compiling (using the browser for gulp). He was expecting XML, where I used array syntax.

I could solve this by creating a method for each component:

 newExampleComponent() { return <ExampleComponent />; } newComponent(type) { return this["new" + type + "Component"](); } 

But this will mean a new method for every component that I create. There should be a more elegant solution to this problem.

I am very open to suggestions.

+113
javascript reactjs react-jsx
Apr 26 '15 at 9:23
source share
11 answers

<MyComponent /> compiles into React.createElement(MyComponent, {}) , which expects a string (HTML tag) or function (ReactClass) to be the first parameter.

You can simply save your component class in a variable with a name that starts with a capital letter. See HTML tags vs React Components .

 var MyComponent = Components[type + "Component"]; return <MyComponent />; 

compiles to

 var MyComponent = Components[type + "Component"]; return React.createElement(MyComponent, {}); 
+114
Apr 26 '15 at 10:07
source share

There is official documentation on how to handle such situations, available here: https://facebook.imtqy.com/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime

It basically says:

Wrong:

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

Right:

 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} />; } 
+98
Nov 30 '16 at 19:01
source share

I understood a new solution. Please note that I am using ES6 modules, so I need a class. You can also define a new React class.

 var components = { example: React.createFactory( require('./ExampleComponent') ) }; var type = "example"; newComponent() { return components[type]({ attribute: "value" }); } 
+10
Apr 26 '15 at 9:58
source share

If your components are global, you can simply do:

 var nameOfComponent = "SomeComponent"; React.createElement(window[nameOfComponent], {}); 
+7
Nov 20 '15 at 20:17
source share

For a wrapper component, a simple solution would be to simply use React.createElement directly (using 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 }) ) } } 
+5
Aug 19 '16 at 18:27
source share

There should be a container that maps component names to all components that should be used dynamically. Component classes must be registered in the container because in a modular environment there is no single place where they could be accessed. Component classes cannot be identified by their names without explicitly specifying them, since the name function is minimized during production.

Component Map

This could be a simple object:

 class Foo extends React.Component { ... } ... const componentsMap = { Foo, Bar }; ... const componentName = 'Fo' + 'o'; const DynamicComponent = componentsMap[componentName]; <DynamicComponent/>; 

Or a Map instance:

 const componentsMap = new Map([[Foo, Foo], [Bar, Bar]]); ... const DynamicComponent = componentsMap.get(componentName); 

A simple object is more appropriate because it benefits from a reduction in properties.

Barrel module

A trunk module with named export can act as such a map:

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

This works well with one class per module code style.

decorator

Decorators can be used with class components for syntactic sugar; for this, you still need to explicitly specify class names and register them in the map:

 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' ... } 

The decorator can be used as a component of a higher order with functional components:

 const Bar = props => ...; Bar.displayName = 'Bar'; export default dynamic(Bar); 

Using a custom displayName instead of a random property also helps debugging.

+1
Apr 21 '19 at 7:08
source share

I used a slightly different approach, since we always know our actual components, so I decided to use the switch case. In addition, the total number of components was not about 7-8 in my case.

 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} /> } } 
0
May 14 '16 at 10:16
source share

Suppose we want to access various views when loading dynamic components. The following code gives a working example of how to do this using a string parsed from a URL search string.

Suppose we want to access the 'snozberrys' page with two unique views using these URLs:

 'http://localhost:3000/snozberrys?aComponent' 

and

 'http://localhost:3000/snozberrys?bComponent' 

we define our view controller as follows:

 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')); 
0
03 Oct '17 at 4:41 on
source share

Please, how can I make this structure in React native?

the component must be from a variable

0
Apr 30 '19 at 10:30
source share

Please, how can I make this structure in React native?

the component must be from a variable

 <Component> <Image /> </Component> 
0
Apr 30 '19 at 10:33
source share

Edit: Other answers are better, see comments.

I solved the same problem as follows:

 ... render : function () { var componentToRender = 'component1Name'; var componentLookup = { component1Name : (<Component1 />), component2Name : (<Component2 />), ... }; return (<div> {componentLookup[componentToRender]} </div>); } ... 
-one
May 23 '16 at 5:39
source share



All Articles