React 中使用 ComponentPropsWithoutRef 扩展 html 元素
有时会需要基于原生 HTML 元素扩展一些自定义 React 组件,这个过程中你需要手工指定需要用到的原生 HTML 元素本机属性,如 onClick
type
等,ComponentPropsWithoutRef
类型可以帮你获取 HTML 元素的所有本机属性作为组件的 props 类型,从而避免手工劳动。
例如,原生 button 元素已经有 onClick 属性,但是当你创建一个 React 组件时,你通常需要使用或像这样为 <Button>
定义:
type ButtonProps = {
children: React.ReactNode
onClick: () => void
}
const Button = ({ children, onClick }: ButtonProps) => {
return <button onClick={onClick}>{children}</button>
}
然后,感觉需求继续为 ButtonProps 添加另一个属性:
type ButtonProps = {
children: React.ReactNode
onClick: () => void
disabled: boolean
type: 'button' | 'submit' | 'reset' | undefined
}
这样一个一个添加就很麻烦,使用 ComponentPropsWithoutRef
类型可以在扩展自定义组件式不需要手工添加这些本机 HTML 属性,
简单地创建一个具有所有本机 button 属性类型的 Button 类型:
type ButtonProps = React.ComponentPropsWithoutRef<"button">
const Button = ({ children, onClick, type }: ButtonProps) => {
return (
<button onClick={onClick} type={type}>
{children}
</button>
)
}
该 ComponentPropsWithoutRef<"button">
类型具有原生 HTML button 元素的所有属性。
如果您想创建一个 <Img>
组件,那么您可以使用 ComponentPropsWithoutRef<"img">
类型:
type ImgProps = React.ComponentPropsWithoutRef<"img">
const Img = ({ src, loading }: ImgProps) => {
return <img src={src} loading={loading} />
}
只需要改变 ComponentPropsWithoutRef<T>
的泛型类型就可以扩展不同的 HTML 元素。例如:
ComponentPropsWithoutRef<'img'>
扩展<img>
元素ComponentPropsWithoutRef<'button'>
扩展<button>
元素ComponentPropsWithoutRef<'a'>
扩展<a>
元素
当你需要添加原生 HTML 元素中不存在的自定义 prop 时,你可以创建一个 interface 扩展原生属性的 prop,如下所示:
interface ImgProps extends React.ComponentPropsWithoutRef<"img"> {
customProp: string;
}
const Img = ({ src, loading, customProp }: ImgProps) => {
// use the customProp here..
return <img src={src} loading={loading} />;
}
如果您需要自定义属性来确定组件的外观,这将特别有用。
在下面的例子中,自定义属性 variant
用于确定 <h1>
元素的 style: color
CSS 属性:
interface headerProps extends React.ComponentPropsWithoutRef<"h1"> {
variant: "primary" | "secondary";
}
const Header = ({ children, variant }: headerProps) => {
return (
<h1 style={{color: variant === "primary" ? "black" : "red" }}>
{children}
</h1>
);
};
该 ComponentPropsWithoutRef
类型可以轻松创建一个作为原生 HTML 元素扩展的组件,而无需自己输入所有可能的 prop 参数。
您甚至可以通过扩展 interface 来添加额外的属性。
当您需要将引用转发给组件的子项时,可以使用 ComponentPropsWithoutRef
接口的孪生兄弟 ComponentPropsWithRef
。
在这里了解更多关于 ref 转发的信息:https://reactjs.org/docs/forwarding-refs.html
ComponentPropsWithoutRef
与 [Element]HTMLAttributes
如果你之前曾在 React 中使用过 TypeScript,那么你可能熟悉 [Element]HTMLAttributes
,你可以使用它来扩展 HTML 元素,如下所示:
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>
type ImgProps = React.ImgHTMLAttributes<HTMLImageElement>
这些 [Element]HTMLAttributes
接口产生与 ComponentPropsWithoutRef
接口相同的类型,但它们更为冗长,因为您需要对每个 HTML 元素使用不同的接口和泛型。
另一方面,ComponentPropsWithoutRef
只需要你改变泛型类型 <T>
。当然,两者都适合在 React 组件中扩展 HTML 元素。