簡體   English   中英

React JSX 中的動態標簽名稱

[英]Dynamic tag name in React JSX

我正在嘗試為 HTML 標題標簽( h1h2h3等)編寫一個 React 組件,其中標題級別是通過道具指定的。

我試着這樣做:

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

我預計輸出如下:

<h1>Hello</h1>

但這不起作用。

有沒有辦法做到這一點?

無法就地執行此操作,只需將其放入變量中(首字母大寫):

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

<CustomTag>Hello</CustomTag>

如果您使用的是 TypeScript,您會看到如下錯誤:

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

TypeScript 不知道CustomTag是一個有效的 HTML 標記名稱並拋出一個無用的錯誤。

要修復,請將CustomTag強制轉換為keyof JSX.IntrinsicElements

// var name must start with a capital letter
const CustomTag = `h${this.props.level}` as keyof JSX.IntrinsicElements;

<CustomTag>Hello</CustomTag>

為了完整起見,如果要使用動態名稱,也可以直接調用React.createElement而不是使用 JSX:

React.createElement(`h${this.props.level}`, null, 'Hello')

這避免了必須創建新的變量或組件。

帶道具:

React.createElement(
  `h${this.props.level}`,
  {
    foo: 'bar',
  },
  'Hello'
)

文檔

創建並返回給定類型的新 React 元素。 type 參數可以是標簽名稱字符串(例如'div''span' ),也可以是 React 組件類型(類或函數)。

使用 JSX 編寫的代碼將被轉換為使用React.createElement() 如果您使用 JSX,通常不會直接調用React.createElement() 請參閱沒有 JSX 的 React以了解更多信息。

所有其他答案都工作正常,但我會添加一些額外的,因為這樣做:

  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;

你可以像這樣使用它

<Heading type="h1">Some Heading</Heading>

或者你可以有一個不同的抽象概念,例如你可以定義一個尺寸道具,如:

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;

你可以像這樣使用它

<Heading size="sm">Some Heading</Heading>

在動態標題(h1, h2...)的實例中,組件可以像這樣返回React.createElement (上面由Felix提到)。

const Heading = ({level, children, ...props}) => {
    return React.createElement(`h${level}`, props , children)
}

為了可組合性,props 和 children 都被傳遞。

查看示例

這就是我為我的項目設置它的方式。

排版類型.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';
};

排版.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;

你可以試試這個。 我是這樣實現的。

import { memo, ReactNode } from "react";
import cx from "classnames";

import classes from "./Title.module.scss";

export interface TitleProps {
  children?: ReactNode;
  className?: string;
  text?: string;
  variant: Sizes;
}

type Sizes = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
const Title = ({
  className,
  variant = "h1",
  text,
  children,
}: TitleProps): JSX.Element => {
  const Tag = `${variant}` as keyof JSX.IntrinsicElements;
  return (
    <Tag
      className={cx(`${classes.title} ${classes[variant]}`, {
        [`${className}`]: className,
      })}
    >
      {text || children}
    </Tag>
  );
};

export default memo(Title);

只想補充一點,如果它們是自定義組件,例如<Foo /><Bar /> 你可以做:

const condition = 'condition here'

const Comp = condition ? <Foo /> : <Bar />

然后在渲染函數中使用<Comp /> ,如下所示:

const render = () => <Comp prop='value' />

使用這種模式的一個常見情況是當多種類型的組件共享相同的props

概括robstarbuck 的答案,您可以創建一個完全動態的標簽組件,如下所示:

const Tag = ({ tagName, children, ...props }) => (
  React.createElement(tagName, props , children)
)

你可以像這樣使用:

const App = ({ myTagName = 'h1' }) => {
  return (
    <Tag tagName={myTagName} className="foo">
     Hello Tag!
    </Tag>
  )
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM