jsx和React中的动态标记名称

Era*_*uwa 138 node.js react-jsx

我尝试编写一个React组件.对于html标题标签(h1,h2,h3等),其中标题优先级根据我们在props中定义的优先级动态变化.

这就是我尝试做的事情.

<h{this.props.priority}>Hello</h{this.props.priority}>

预期产量:

<h1>Hello</h1>

这不起作用.有没有可行的方法来做到这一点?

zer*_*kms 279

无法在原地进行,只需将其放入变量(首字母大写):

const CustomTag = `h${this.props.priority}`;

<CustomTag>Hello</CustomTag>
Run Code Online (Sandbox Code Playgroud)

  • 如果组件存储在对象的属性中,则不需要大写首字母.`var foo = {bar:CustomTag}; return <foo.bar />`工作正常. (4认同)
  • 绝对比`React.createClass`更容易,我更喜欢这种方式.谢谢. (3认同)

Jac*_*eam 72

如果您使用 TypeScript,您会看到如下错误:

Type '{ children: string; }' has no properties in common with type 'IntrinsicAttributes'.ts(2559)

TypeScript 不知道这CustomTag是一个有效的 HTML 标签名称,并抛出一个无用的错误。

要修复,请强制转换CustomTagkeyof JSX.IntrinsicElements

const CustomTag = `h${this.props.priority}` as keyof JSX.IntrinsicElements;

<CustomTag>Hello</CustomTag>
Run Code Online (Sandbox Code Playgroud)

  • 我认为 ```const Tag: keyof JSX.IntrinsicElements = `h${level}`;``` 会稍微好一点,因为如果你现在使用无效的标签,例如 `headline${level}` TypeScript 会抱怨。(假设“level”正确输入为文字类型) (6认同)
  • 只是想对此表示感谢。如果不在这里,我可能会花几个小时来尝试输入此内容。 (4认同)
  • 我使用 TypeScript,但转换它会出现此错误:`属性“crossOrigin”的类型不兼容。输入“字符串|” undefined' 不可分配给类型 '"" | “匿名”| “使用凭证”| 不明确的'。类型“string”不可分配给类型“”” | “匿名”| “使用凭证”| 未定义'。` (2认同)

Fel*_*ing 25

为了完整性,如果要使用动态名称,也可以直接调用React.createElement而不是使用JSX:

React.createElement(`h${this.props.priority}`, null, 'Hello')
Run Code Online (Sandbox Code Playgroud)

这避免了必须创建新变量或组件.

有道具:

React.createElement(
  `h${this.props.priority}`,
  {
    foo: 'bar',
  },
  'Hello'
)
Run Code Online (Sandbox Code Playgroud)

来自文档:

创建并返回给定类型的新React元素.type参数可以是标记名称字符串(例如'div'or 'span'),也可以是React组件类型(类或函数).

用JSX编写的代码将转换为使用React.createElement().React.createElement()如果您使用的是JSX,通常不会直接调用.请参阅React Without JSX以了解更多信息.


rob*_*uck 12

在动态标题(h1, h2...)的实例中,组件可以像这样返回(上面由FelixReact.createElement提到)。

const Heading = ({level, children, ...props}) => {
    return React.createElement('h'.concat(level), props , children)
}
Run Code Online (Sandbox Code Playgroud)

为了可组合性,道具和子项都被传递。

参见示例


Sam*_*igh 5

所有其他答案都工作正常,但我会添加一些其他内容,因为这样做:

  1. 这比较安全。即使类型检查失败,您仍然会返回适当的组件。
  2. 它更具声明性。通过查看此组件的任何人都可以看到它可以返回什么。
  3. 例如,它更灵活,而不是“ h1”,“ h2”……对于标题的类型,您可以具有一些其他抽象概念“ sm”,“ lg”或“ primary”,“ secondary”

标题组件:

import React from 'react';

const elements = {
  h1: 'h1',
  h2: 'h2',
  h3: 'h3',
  h4: 'h4',
  h5: 'h5',
  h6: 'h6',
};

function Heading({ type, children, ...props }) {    
  return React.createElement(
    elements[type] || elements.h1, 
    props, 
    children
  );
}

Heading.defaultProps = {
  type: 'h1',
};

export default Heading;
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它

<Heading type="h1">Some Heading</Heading>
Run Code Online (Sandbox Code Playgroud)

或者您可以有一个不同的抽象概念,例如,您可以定义一个尺寸道具,例如:

import React from 'react';

const elements = {
  xl: 'h1',
  lg: 'h2',
  rg: 'h3',
  sm: 'h4',
  xs: 'h5',
  xxs: 'h6',
};

function Heading({ size, children }) {
  return React.createElement(
    elements[size] || elements.rg, 
    props, 
    children
  );
}

Heading.defaultProps = {
  size: 'rg',
};

export default Heading;
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它

<Heading size="sm">Some Heading</Heading>
Run Code Online (Sandbox Code Playgroud)


Mik*_*ike 5

这就是我为我的项目设置的方式。

TypographyType.ts

import { HTMLAttributes } from 'react';

export type TagType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';

export type HeadingType = HTMLAttributes<HTMLHeadingElement>;
export type ParagraphType = HTMLAttributes<HTMLParagraphElement>;
export type SpanType = HTMLAttributes<HTMLSpanElement>;

export type TypographyProps = (HeadingType | ParagraphType | SpanType) & {
  variant?:
    | 'h1'
    | 'h2'
    | 'h3'
    | 'h4'
    | 'h5'
    | 'h6'
    | 'body1'
    | 'body2'
    | 'subtitle1'
    | 'subtitle2'
    | 'caption'
    | 'overline'
    | 'button';
};
Run Code Online (Sandbox Code Playgroud)

版式.tsx

    import { FC } from 'react';
    import cn from 'classnames';
    import { typography } from '@/theme';
    
    import { TagType, TypographyProps } from './TypographyType';
    
    const headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
    const paragraphs = ['body1', 'body2', 'subtitle1', 'subtitle2'];
    const spans = ['button', 'caption', 'overline'];
    
    const Typography: FC<TypographyProps> = ({
      children,
      variant = 'body1',
      className,
      ...props
    }) => {
      const { variants } = typography;
    
      const Tag = cn({
        [`${variant}`]: headings.includes(variant),
        [`p`]: paragraphs.includes(variant),
        [`span`]: spans.includes(variant)
      }) as TagType;
    
      return (
        <Tag
          {...props}
          className={cn(
            {
              [`${variants[variant]}`]: variant,
            },
            className
          )}
        >
          {children}
        </Tag>
      );
    };
    
    export default Typography;
Run Code Online (Sandbox Code Playgroud)