如何根据道具动态生成带有标签名称的元素?

时间:2017-07-02 05:14:00

标签: javascript html reactjs react-jsx

我有兴趣为我的特定目的构建可重用的<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 tagNames到相应的JSX语法的相当冗长的映射驱动的。我最喜欢使这个组件支持所有非自动关闭的HTML元素,但显然希望这样做而不必强制扩展此映射以包含符合该标准的每个可能的HTML元素。

是否有更具活力,面向未来的方法,如果是这样,那可能是什么样的?

1 个答案:

答案 0 :(得分:1)

如果我理解正确,你只需为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}`);
  }
}