如何让 react 组件支持 html 的所有属性
实现
如果让你封装一个 Button 组件,需要支持 onClick 事件,但 onClick 不是必传,你会怎么做?很简单地,我们可以写出以下的代码
interface ButtonProps {
text: string
onClick?: () => void
}
const Button = ({ text, onClick }: ButtonProps): JSX.Element => (
<button onClick={() => {
if (onClick !== undefined) {
onClick()
}
}}>
{text}
</button>
)
可以看到,我们还得需要判断 onClick 是否传递来避免点击报错,如果要支持 touch 事件,那又该怎么做呢,touch 事件可是比较复杂的,onTouchStart
, onTouchMove
, onTouchEnd
,势必会使代码膨胀
巧妙地利用展开语法可解决这个问题,写法也非常简单
interface ButtonProps {
text: string
[key: string]: any
}
const Button = ({ text, ...rest }): JSX.Element => (
<button {...rest}>{ text }</button>
)
这样,react 组件就能支持 html 的所有属性了
获取类型提示
上述的代码还有一个问题,那就是没有类型提示,如果你把鼠标停在 JSX 的代码比如 <div>
,是可以看到弹出 popup ,里面有 (property) JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
的信息,类型提示就是依赖于 HTMLAttributes<HTMLDivElement>
,更准确的说法是依赖于 HTMLAttributes
这个泛型接口。因此,或继承或联合,React 组件就可以获取代码提示了
import { HTMLAttributes } from "react";
// 继承
interface ButtonProps extends HTMLAttributes<HTMLButtonElement> {
text: string
}
const Button = ({
text,
...rest
}: ButtonProps): JSX.Element => (
<button {...rest}>{text}</button>
);
// 联合
interface DivProps {
text: string;
}
const Div = ({ text, ...rest }: DivProps & HTMLAttributes<HTMLDivElement>): JSX.Element => (
<div {...rest}>{text}</div>
);
我们也可以搞个如内置高级类型 Record
的泛型类型
type ComponentProps<T1 extends object, T2 extends HTMLElement> = T1 & HTMLAttributes<T2>;
const Span = ({
text,
...rest
}: ComponentProps<{ text: string }, HTMLSpanElement>): JSX.Element => (
<span {...rest}>{text}</span>
);
如果你使用的不是 typescript ,那又该怎么做呢?这时,就需要拿出 JsDoc 了,当然 JsDoc 的玩法相比 ts 就少了很多
/**
*
* @param {{text: string} & import('react').HTMLAttributes<HTMLDivElement>} props
* @returns
*/
const Div = ({ text, ...rest }) => <div {...rest}>{text}</div>;
/**
*
* @param {{text: string} & import('react').HTMLAttributes<HTMLButtonElement>} props
* @returns
*/
const Button = ({ text, ...rest }) => <button {...rest}>{text}</button>;
通过上述的做法,我们就获得了不错的开发体验
转载自:https://juejin.cn/post/7141774744117837837