使用TypeScript编写可复用的React组件

1. 定义组件

创建可重用的组件的第一步是识别它。首先去寻找原子组件,像button、input等等。然后去使用原子组件来制作templates,使用templates来制作pages

下面的代码片段是一简单的button组件。使用HTMLAttributes来获取button元素的默认属性类型

/* Button.module.css */
.btn {
padding: 0.5rem 1rem;
}
// Button.tsx
import { HTMLAttributes } from "react";
import styles from "./Button.module.css";
type TProps = HTMLAttributes<HTMLButtonElement>;
export const Button = ({ children, ...props }: TProps) => {
return (
<button className={styles.btn} {...props}>
{children}
</button>
)
}

2. 定义组件的状态

当开发一个React,需要定义它的states。 就像Button组件可以enabled或者disabled,可以是primary或者是secondary。同一时间内,一个按钮可以有多个状态,就像Button可以是主状态并处于禁用状态一样。我们会使用自定义属性来改变组件的状态。

下面的代码片段展示了带有variety和isDisabled可选属性的Button组件。我们将基于这些属性来改变按钮的状态。

/* Button.module.css */
.btn {
padding: 0.5rem 1rem;
}
.secondaryBtn {
background-color: #5f5757;
color: rgb(21, 5, 5);
}
import { HTMLAttributes } from "react";
import styles from "./Button.module.css";
type TProps = HTMLAttributes<HTMLButtonElement> & {
variety?: "primary" | "secondary";
isDisabled?: boolean;
};
export const Button = ({
children,
variety = "primary",
isDisabled = false,
...props
}: TProps) => {
return (
<button
className={`${styles.btn} ${variety === "secondary" && styles.secondaryBtn}`}
disabled={isDisabled}
{...props}>
{children}
</button>
)
}

3. 不要给组件设定宽度、高度和margin

我们不知道组件会用在哪里。所以你可以给一个最小宽度或者最小高度而不是设定固定的宽度或者高度。你可以给宽度设置成100%,或者传入className或者style属性,这样我们通过传入这些属性来改变style

下面的代码片段中通过传入className和style来改变Button组件的样式

// Button.tsx
import { HTMLAttributes } from "react";
import styles from "./Button.module.css";
type TProps = HTMLAttributes<HTMLButtonElement> & {
variety?: "primary" | "secondary";
isDisabled?: boolean;
};
export const Button = ({
children,
variety = "primary",
isDisabled = false,
className,
style,
...props
}: TProps) => {
return (
<button
style={style}
className={`${styles.btn} ${
variety === "secondary" && styles.secondaryBtn
} ${className}`}
disabled={isDisabled}
{...props}>
{children}
</button>
)
}

4. 尽可能的减少组件的State

如果组件中有一些逻辑需要执行,比如执行Button组件的单击操作。不要在组件内部编写处理函数,而是将其作为props传递。尝试将所有依赖项作为props传递。保持组件的无状态是一个很好的实践。

// Button.tsx
import { HTMLAttributes } from "react";
import styles from "./Button.module.css";
type TProps = HTMLAttributes<HTMLButtonElement> & {
variety?: "primary" | "secondary";
isDisabled?: boolean;
};
export const Button = ({
children,
variety = "primary",
isDisabled = false,
className,
style,
...props
}: TProps) => {
return (
<button
style={style}
className={`${styles.btn} ${
variety === "secondary" && styles.secondaryBtn
} ${className}`}
disabled={isDisabled}
{...props}>
{children}
</button>
);
};
// index.tsx
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";
import { Button } from "../components/Button";
const Home: NextPage = () => {
const handleClick = () => {
setTimeout(() => {
alert("Clicked!");
}, 1000);
};
return (
<div className={styles.container}>
<Button variety="secondary" onClick={handleClick}>
Click
</Button>
</div>
);
};
export default Home;

参考阅读