以显式简化封装复杂公共组件的三要素
2023-11-13 14:01:58
序
当我们进行组件封装时,我们一般需要实现两个主要的功能:
- 可复用:封装成可以被其他程序使用
- 可配置:提供多种方式让用户进行配置。
在本文中,我们将以开发表单组件库为案例,对输入框组件实现一个最简单的属性配置封装,通过这一个组件的封装,去理解公共组件封装中最核心的三要素。
在进行以下步骤之前,请确保你已经了解了诸如函数式组件 、钩子函数 、声明状态 等基本概念。
封装组件
import React, { useState } from "react";
export function Input(props: any) {
// 将placeholder进行类型校验
const _placeholder = typeof props.placeholder === "string" ? props.placeholder : '';
// 使用useState声明一个状态,来保存输入的内容
const [value, setValue] = useState(props.value ? props.value : '');
// 输入框的onChange事件
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// 将输入的值赋给value这个状态
setValue(event.target.value);
};
return (
<input
{...props}
value={value}
placeholder={_placeholder}
onChange={handleChange}
/>
);
}
在进行组件封装时,我们需要仔细思考一个问题,组件的状态应该在哪里声明 。
我们知道,状态 是唯一可以改变的 。那么在开发组件时,状态是一个很容易导致父组件与子组件之间的逻辑混乱的点。我们通常需要认真考虑状态的声明位置。
比如,对于这个输入框组件而言,如果我们希望支持受控组件,也就是从父组件传递value来控制输入框的状态,那么我们就不能在组件内部声明状态。
同时,考虑到后续我们会封装一个表单组件,我们需要在表单组件内部支持表单受控,也就是从表单组件外部传递值来修改表单内部所有输入框的值。这样一来,我们也不应该在输入框内部声明状态。
所以,在最终实现这个组件的组件库时,我们通过组合继承,在父组件内部声明组件状态,这样就实现了一个通用的可配置输入框组件。
声明属性与状态
接下来,我们要思考的是组件的属性与状态,我们应该如何进行声明。
首先,我们先明确 属性 与 状态 的区别。
属性 是从外部组件传递过来的值,在组件内部使用这些属性值进行组件渲染,组件并不能修改自己的属性 。
状态 是唯一可以改变的 ,它由组件内部维护,可以被组件自身通过一些操作进行修改。
我们从这个输入框组件的角度来说,它的属性一般包括:
- id
- placeholder
- type
- value
而它的状态一般包括:
- 输入框的值
- 是否有焦点
- 是否被禁用
- 是否正在提交
显而易见,这个组件的属性与状态是非常多的。
在声明属性与状态时,我们需要秉持着最少原则 。
我们应该只在组件内部声明和使用那些必须 的属性与状态,而不是把所有可能的情况都考虑进来。我们更希望的做法是根据实际情况进行属性与状态的声明。
例如,在实现这个输入框组件时,我们仅支持受控组件,也就是状态全部由外部组件声明,在这个场景下,组件内部完全没有必要声明状态。
export function Input(props: any) {
const _placeholder = typeof props.placeholder === "string" ? props.placeholder : '';
return (
<input
{...props}
placeholder={_placeholder}
/>
);
}
暴露组件
完成属性和状态的声明之后,我们就可以导出组件了。
如果我们写一个npm包,那么在index.js文件里我们需要导出这个组件,在最终的发布包里,用户可以通过 import Input from 'xxx' 来使用这个组件。
export { Input };
总结
在本文中,我们通过开发表单组件库的案例,理解了组件封装中最核心的三要素:
- 组件的状态应该在哪里声明
- 组件的属性与状态应该如何进行声明
- 暴露组件
我们希望通过这三个要素的实现,可以帮你去开发出一个可复用,且可配置性较强的公共组件库。