I'd like to create a super general component that can be built with any element type. ie, I'd like to do something like this:
const Wrapper = React.createClass({
render() {
const { props } = this;
const elementType = props.elementType;
return (
<{elementType} className={className}>
{props.children}
</{elementType}>
);
}
});
but of course React gets very angry when I attempt this. Is there any way to build a component with to-be-decided HTML types?
You can use React.createElement to accomplish that (after all, JSX just provides syntactic sugar for it).
Something like that:
const myElement = React.createElement(
/* type */
props.elementType, //must be a string or a React Component class
/* props object */
{
className: className
},
/* You can pass children to the third parameter also. */
props.children
);
return myElement;
This depends very much on what elementType is. In JSX, you can either pass in a custom component itself, or a string (e.g. h1, p) as a "tag". In any case, you don't want to put the curly brackets {} around the tag. Very simple example:
// Usually you want to import this. (This is just a stateless component)
const Element = (props) => <em>{props.children}</em>;
class Example extends React.Component {
constructor() {
super();
this.state = { element1: 'h1', element2: Element };
}
render() {
return(
<div>
<this.state.element1>Hello!</this.state.element1>
<this.state.element2>World!</this.state.element2>
</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById('View'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='View'></div>
If you want to use a string as a reference to a custom component, you will need to use native JavaScript with React's createElement/cloneElement.
Yes, you can — even with JSX if you'd like.
The trick is to understand how JSX is translated into the React.createElement calls:
<div/> becomes React.createElement('div').<SomeComponent/> becomes React.createElement(SomeComponent).Now imagine if you assign var SomeComponent = 'div'. Then the uppercase rule would apply (passing the variable to createElement), but it will have the same effect as the first rule (because the variable references a string)!
Here's how it can look in a practical situation:
let Child = ({EL,thing}) => <EL className="helper-thing">{{ thing.name }}</EL>
let ListParent = ({things}) => <ul>
{things.map(t => <Child EL='li' thing={t}/>)}
</ul>
let ArticleParent = ({things}) => <article>
{things.map(t => <Child EL='p' thing={t}/>)}
</article>
See the official docs on Choosing the Type at Runtime for a slightly different — perhaps cleaner — approach that capitalizes the property inside the child component.