如何根据道具动态生成带有标签名称的元素?
How do I dynamically generate an element with a tag name based on a prop?
我有兴趣为我的特定目的构建一个可重用的 <Element>
React 组件。目的是让任何使用它的人都可以选择指定一个 tagName
道具,该道具指示组件将编译到的确切 HTML 元素。就目前而言,Element
组件如下所示:
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));
}
};
然而,正如所见,该组件能够成功工作的能力是由 HTML tagName
s 与其对应的 JSX 语法的相当冗长的映射驱动的。我最希望使该组件支持所有非自闭合 HTML 元素,但显然我希望这样做而不必被迫扩展此映射以包含所有可能的 HTML 元素拟合该标准。
是否有更动态、面向未来的方法来这样做?如果有,那会是什么样子?
如果我没理解错的话,你只是在为 React.createElement
创建一个包装器,所有 JSX 都被转换成这个包装器来创建你的元素:
render() {
const {
children,
tagName = 'div',
...rest
} = this.props;
return React.createElement(tagName, rest, children);
}
因此:
<Element tagName="span" foo="bar">
Foobar!
</Element>
将成为(一旦转译):
React.createElement('span', {
foo: 'bar'
}, 'Foobar!');
与以下相同:
<span foo="bar">Foobar!</span>
如果您只想将其限制为 DOM 元素而不是自定义 React 组件,只需限制 proptype 或自己验证类型:
tagName: PropType.string.isRequired
甚至您自己的验证器:
tagName: (props, propName, componentName) => {
if(typeof props[propName] !== 'string') {
return new Error(`The ${propName} prop should be a string in ${componentName}`);
}
}
我有兴趣为我的特定目的构建一个可重用的 <Element>
React 组件。目的是让任何使用它的人都可以选择指定一个 tagName
道具,该道具指示组件将编译到的确切 HTML 元素。就目前而言,Element
组件如下所示:
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));
}
};
然而,正如所见,该组件能够成功工作的能力是由 HTML tagName
s 与其对应的 JSX 语法的相当冗长的映射驱动的。我最希望使该组件支持所有非自闭合 HTML 元素,但显然我希望这样做而不必被迫扩展此映射以包含所有可能的 HTML 元素拟合该标准。
是否有更动态、面向未来的方法来这样做?如果有,那会是什么样子?
如果我没理解错的话,你只是在为 React.createElement
创建一个包装器,所有 JSX 都被转换成这个包装器来创建你的元素:
render() {
const {
children,
tagName = 'div',
...rest
} = this.props;
return React.createElement(tagName, rest, children);
}
因此:
<Element tagName="span" foo="bar">
Foobar!
</Element>
将成为(一旦转译):
React.createElement('span', {
foo: 'bar'
}, 'Foobar!');
与以下相同:
<span foo="bar">Foobar!</span>
如果您只想将其限制为 DOM 元素而不是自定义 React 组件,只需限制 proptype 或自己验证类型:
tagName: PropType.string.isRequired
甚至您自己的验证器:
tagName: (props, propName, componentName) => {
if(typeof props[propName] !== 'string') {
return new Error(`The ${propName} prop should be a string in ${componentName}`);
}
}