Reputation: 4481
I'm interested in building a reusable <Element>
React component for my particular purposes. The intent is for whomever using it to optionally specify a tagName
prop that dictates the exact HTML element into which the component will compile. As it stands, here is what the Element
component looks like:
import React, { Component } from 'react';
class Element extends Component {
constructor(props) {
super(props);
this.mapToElement = new Map([
['ARTICLE', (props, children) => (<article {...props}>{children}</article>)],
['ASIDE', (props, children) => (<aside {...props}>{children}</aside>)],
['DIV', (props, children) => (<div {...props}>{children}</div>)],
['FOOTER', (props, children) => (<footer {...props}>{children}</footer>)],
['HEADER', (props, children) => (<header {...props}>{children}</header>)],
['MAIN', (props, children) => (<main {...props}>{children}</main>)],
['P', (props, children) => (<p {...props}>{children}</p>)],
['SECTION', (props, children) => (<section {...props}>{children}</section>)],
['SPAN', (props, children) => (<span {...props}>{children}</span>)]
]);
}
render() {
const {
children,
tagName = 'DIV',
...rest
} = this.props;
return (this.mapToElement.get(tagName.toUpperCase())(rest, children));
}
};
As can be seen, however, the ability for this component to successfully work is driven by a rather verbose mapping of HTML tagName
s to their corresponding JSX syntax. I'd optimally like to make this component have support for all non-self-closing HTML elements, but would obviously like to do so without being forced to expand this mapping to include every possible HTML element fitting that criterion.
Is there a more dynamic, future-proof means for doing so and, if so, what might that look like?
Upvotes: 1
Views: 476
Reputation: 57964
If I understand you correctly, you're just creating a wrapper for React.createElement
which is what all JSX is transpiled into to create your elements:
render() {
const {
children,
tagName = 'div',
...rest
} = this.props;
return React.createElement(tagName, rest, children);
}
Thus:
<Element tagName="span" foo="bar">
Foobar!
</Element>
Will become (once transpiled):
React.createElement('span', {
foo: 'bar'
}, 'Foobar!');
Which is the same as:
<span foo="bar">Foobar!</span>
And if you only ever wanted to restrict it to just DOM elements and not custom React components, just restrict the proptype or validate the type yourself:
tagName: PropType.string.isRequired
Or even your own validator:
tagName: (props, propName, componentName) => {
if(typeof props[propName] !== 'string') {
return new Error(`The ${propName} prop should be a string in ${componentName}`);
}
}
Upvotes: 1