[英]Polymorphic component - selecting type from a prop in React with Typescript
這是我目前正在處理的一個組件,名為TextBody
import { HTMLAttributes } from "react";
import classNames from "classnames";
interface TextBodyProps
extends HTMLAttributes<HTMLParagraphElement | HTMLSpanElement> {
span?: boolean;
type: "s" | "m";
}
export const TextBody = ({
span,
type,
className,
children,
...props
}: TextBodyProps) => {
const textBodyClassNames = {
s: "text-body-s font-light leading-relaxed max-w-sm",
m: "text-body-m font-light leading-relaxed max-w-sm",
};
const TextBodyElement = span ? "span" : "p";
return (
<TextBodyElement
{...props}
className={classNames(textBodyClassNames[type], className)}
>
{children}
</TextBodyElement>
);
};
如果傳遞了span
屬性,是否可以擴展HTMLAttributes<HTMLSpanElement>
,如果不是,則只擴展 HTMLAttributes HTMLAttributes<HTMLParagraphElement>
,而不是聯合?
這對於評論來說太長了,可能是一個答案。 我會將其編碼如下:
import { HTMLAttributes } from "react";
import classNames from "classnames";
// This is a more accurate union: you have *either* the span
// attributes *or* the paragraph attributes, not a union of the
// attributes of both.
type TextBodyProps = (HTMLAttributes<HTMLParagraphElement> | HTMLAttributes<HTMLSpanElement>) & {
span?: boolean;
type: "s" | "m";
};
export const TextBody = ({
span,
type,
className,
children,
...props
}: TextBodyProps) => {
return span ? (
<span
{...props}
className={classNames("text-body-s font-light leading-relaxed max-w-sm", className)}
>
{children}
</span>
) : (
<p
{...props}
className={classNames("text-body-m font-light leading-relaxed max-w-sm", className)}
>
{children}
</p>
);
};
有一點重復嗎? 是的。 但是一點點重復總比過早抽象、 YAGNI等要好。原始示例中的那個很難實現。 也許這里有一種既能吃又能吃蛋糕的優雅方式,但我會首先從簡單、易於實現、易於閱讀的版本開始。
這是我想出的最終解決方案:
import { ElementType } from "react";
import classNames from "classnames";
import { PolymorphicComponentProps } from "../../types";
type Variants = {
s: string;
m: string;
};
type TextBodyProps = {
type: keyof Variants;
};
const textBodyClassNames: Variants = {
s: "text-body-s font-light leading-relaxed",
m: "text-body-m font-light leading-relaxed",
};
const defaultComponent = "span";
export const TextBody = <
Component extends ElementType = typeof defaultComponent
>({
as,
type,
className,
children,
...props
}: PolymorphicComponentProps<Component, TextBodyProps>) => {
const Component = as || defaultComponent;
return (
<Component
{...props}
className={classNames(textBodyClassNames[type], className)}
>
{children}
</Component>
);
};
我認為它非常清楚如何通過添加新變體、更改變體樣式或更改默認組件(如果未指定)來擴展它。
PolymorphicComponentProps 文件包含:
import {
ComponentPropsWithoutRef,
PropsWithChildren,
ElementType,
} from "react";
type AsProp<Component extends ElementType> = {
as?: Component;
};
type PropsToOmit<
Component extends ElementType,
Props
> = keyof (AsProp<Component> & Props);
export type PolymorphicComponentProps<
Component extends ElementType,
Props = {}
> = PropsWithChildren<Props & AsProp<Component>> &
Omit<ComponentPropsWithoutRef<Component>, PropsToOmit<Component, Props>>;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.