0%

React组件库实现——Button组件

我们以一个Button组件为例。尝试从设计到实现一个React组件。

按钮的设计

轮廓线(按钮的边框)

主要的作用就是按钮与背景色相近的时候让按钮更加的明显。
成像效果不好的显示器,显示效果会有偏差。加上轮廓线确保显示。

类型检测

静态类型检测是写组件必不可少的。一方面是在开发时避免类型问题,它还可以通过IDE的智能提醒方便使用组件的开发者。将使用Typescript对组件进行类型检测声明。

Props声明

首先是Props声明,Props声明我们用组件名后面跟Props的形式来定义接口名称,并将它导出。

1
2
3
4
5
export interface BaseButtonProps {
type?: ButtonType;
children?: React.ReactNode;
...
}

在定义props的时候,我们要保持接口小,props数量要少。

组件声明

在组件使用中,应该无状态组件>有状态组件>Class组件。尤其是在hook出现后,函数组件被React大肆推广。

函数组件

我们用FC来声明函数组件,FC就是FunctionComponent的缩写,我们可以通过源码看到,它的props默认定义了children。

1
2
3
const ButtonBase:FC<BaseButtonProps> = props => {
return <div>{props.children}</div>
}

然后将这个组件导出

1
export default Button;

但是在之前FC类型来声明函数组件时并不能完美支持propsDefault(现在已经解决)。
一般用解构赋值的默认值来代替propsDefault

1
2
3
4
const BaseButton:FC<BaseButtonProps> = props => {
const { type = "default" } = props;
return <div>{props.children}</div>
}

也有直接使用普通函数来进行组件声明

1
2
3
4
const BaseButton = (props: BaseButtonProps): JSX.Element => {
const { type = "default" } = props;
return <div>{type}</div>
}

用普通函数来进行组件声明还有以下好处:

  1. React.FC隐式的提供了一个children props,如果你的组件不需要子组件,用普通函数组件也许是一个很好的选择
  2. 支持泛型。例如

    1
    2
    3
    4
    5
    6
    7
    type OptionValue = string | number;

    type Option<T extends OptionValue> = {
    value: T;
    };

    const App = <T extends OptionValue>(props: Option<T>): JSX.Element => <div>{props.value}</div>
  3. 在使用defaultProps不会出错。

【注意】在使用普通函数来进行组件声明的时候,defaultProps的类型和组件本身的props没有关联性,会使得defaultProps无法得到类型约束。比如

1
2
3
4
5
6
7
interface ButtonProps {
name: string;
}

const Button = (props: ButtonProps) => <div>{props.name}</div>

Button.defaultProps = { name: 123 } //这样是不会报错的

我们需要改写成这样

1
2
3
4
5
type Partial<T> = {
[P in keyof T]?: T[P];
};

Button.defaultProps = { name: 'Liam' } as Partial<ButtonProps>