React的对象式组件,以及泛型组件的应用
在写 react 的时候,用到比如
antd
等UI库,多少会遇到类似于下面这种写法
<Form.Item name="addr" label="站点地址" style={{ margin: 0 }}>
<Input allowClear disabled={!isEditInfo} />
</Form.Item>
我们习惯了
<Component {...props} />
的写法,再看上面的组件,如果不了解,难免会一脸懵。其实理解了也很简单,就是万物皆对象
下面分几个步骤来实现上面Form.Item
形式的组件
对象式
1. 常规组件
// 封装
class TestA extends React.Component<Props> {
render() {
return <div className="test-a">TestA--{this.props?.name}</div>
}
}
// 使用
export default ()=>{
return < TestA name={'对象式组件'} />
}
上面就是一个简单的react组件封装以及使用,要实现组件式,其实很简单,把封装的组件放在一个对象中就可以实现
2. 对象式封装
// 把封装的组件放到一个对象中,作为对象的属性
const Test = {
TestA
}
使用的时候,就可以按预期效果来用了
// 使用
export default ()=>{
return <Test.TestA name={'对象式组件'} />
}
是不是很简单。
这其实是得益于ReactJSX的语法,在reactjsx语法中一切皆对象,同时也一切皆组件,所以就有了上面的组件写法。
泛型组件
1. 常规组件
接着上面的组件,写一个新的组件TestB
function TestB({ name, name2 }: { name: string, name2?: string }) {
return <div className="test-b">TestB--{name}{name2}</div>
}
这个组件的name
属性都是指定了传参格式,如果想不指定,而是想通过传入参数的类型去推导实际类型,这就用到泛型。
2. 泛型组件
如果这个组件想用到泛型,也就是标题所说的泛型组件,其实也很简单
function TestB<T>({ name, name2 }: { name: T, name2?: T }) {
return <div className="test-b">TestB--{name}{name2}</div>
}
想上面一样,只要给组件TestB
一个类型参数,后面用到的同一个类型变量,就会根据传入的参数类型自动推导,这就是泛型。
比如我给组件
TestB
指定一个类型type TestType = string
,使用的时候如下
export default ()=>{
return <TestB<TestType>
name2={undefined} // name2 的类型同样是由 TestType 决定,因为 name2 是可选属性,所以可以为 undefined
name={'组件上使用泛型'}
/>
}
上面的
<TestType>
就是相当于给组件TestB
指定了传入的props都只能用string
。
有一点需要注意的是,TestType
在这里不能用对象类型声明或者接口声明,否则只能如下面那样使用
interface TestType {
name: string
name2: number | undefined
}
export default ()=>{
return <Test.TestB<TestType>
name2={undefined} // name2 的类型同样是由 TestType 决定,因为 name2 是可选属性,所以可以为 undefined
name={{
name: '组件上使用泛型',
name2: 123
}}
/>
}
怎么样,是不是很简单。
如果不理解的人,刹时间看到<Test.TestB<TestType> {...props} />
这种写法多少会有点懵,看完这边文章应该多少能理解一些了。
总结
上面就是本文对象组件以及泛型组件的实现。
前者容易理解所以无需多说,但是对于泛型,组合使用的好,对于封装特别是高阶组件的封装能够起到很强大的类型约束效果,而本文只是浅显的应用。
无规矩不成方圆,使用TS
尽管过程会显得麻烦,并且会显著增加工作量,但是习惯了甚至是得心应手的时候,这绝对是日常开发上的一大助力。