likes
comments
collection
share

如何让 react 组件支持 html 的所有属性

作者站长头像
站长
· 阅读数 88

实现

如果让你封装一个 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
评论
请登录