原生组件是对公共场景的抽象,若要契合实际业务往往需要对其进行二次包装。
对组件进行二次包装一般需要进行包括不限于以下的步骤:
- 组件类型类型声明
- 自定义组件渲染逻辑
- 向原生组件透传属性
1. 类型声明
类型声明是组件二次包装过程中的第一步,项目中常见的方案是直接进行类型声明,例如通过Button组件包装个自定义的Button:
import React, { CSSProperties } from "react"; import { Button } from "@arco-design/web-react"; type IProps = { style?: CSSProperties; className?: string | string[]; type?: "default" | "primary" | "secondary" | "dashed" | "text" | "outline"; // ...其他Button属性 }; const MyButton: React.FC<IProps> = ({ style, className, type, children }) => { // 此处为自定义逻辑 return ( <Button style={style} className={className} type={type}> {children} </Button> ); }; export default MyButton;
直接类型声明简单粗暴,用到什么属性就添加什么,但存在一个致命的,其限定了原生组件提供的能力,降低了二次包装的自定义组件的可用性。
因此更为好的方案是类型继承,不仅继承类型,也继承属性:
import React from "react"; import { Button, ButtonProps } from "@arco-design/web-react"; // 继承类型 type IProps = ButtonProps & {}; const MyButton: React.FC<IProps> = (props) => { // 此处为自定义逻辑 // 继承属性 return <Button {...props} />; }; export default MyButton;
2. 默认属性
开源组件库提供的组件满足了日常项目的开发需求,但开源项目毕竟是面对公共场景,其组件默认值并不能匹配实际的业务场景,因此我们通常需要在二次包装自定义组件的时候重置默认值。
常见的重置方案也是直接重置:
import React from "react"; import { Button, ButtonProps } from "@arco-design/web-react"; type IProps = ButtonProps & {}; const MyButton: React.FC<IProps> = (props) => { const { size = "large", type = "primary" } = props; return <Button size={size} type={type} {...props} />; }; export default MyButton;
这种方式很直观,也没有任何问题。但追求极致的各位大佬们,肯定在重置多个自定义组件的过程中厌烦了这种重复的写法。
因此更好的方式是使用高阶组件,首先先定义一个通用的Hoc:
import React from "react"; /** * 组件默认属性Hoc * @param defaultProps 默认属性 * @returns */ export function withDefaultProps<P = {}>(defaultProps?: Partial<P>) { return function (Component: any) { const WrappedComponent: React.FC<P> = (props) => { return React.createElement(Component, { ...defaultProps, ...props, }); }; WrappedComponent.displayName = `withDefaultProps(${getDisplayName( Component )})`; return WrappedComponent; }; }
这时候属性重置就会变得有亿点点优雅了:
import React from "react"; import { Button, ButtonProps } from "@arco-design/web-react"; type IProps = ButtonProps & {}; export default withDefaultProps<IProps>({ size: "large", type: "primary", })(Button);
通过组件默认属性Hoc可以有效抽离包装业务组件,优化代码结构。
3. 自定义属性与属性透传
二次包装组件的当然不仅仅只包含原生组件的属性,还有新增的自定义属性,例如下面定义的组件:
import React, { useEffect } from "react"; import { Button, ButtonProps } from "@arco-design/web-react"; type IProps = ButtonProps & { hello?: string; }; const MyButton: React.FC<IProps> = (props) => { useEffect(() => console.log(props.hello), [props.hello]); return <Button {...props} />; }; export default withDefaultProps<IProps>({ hello: "world", })(MyButton);
上述代码中未过滤自定义属性,可能会导致原生组件接收非声明的属性导致渲染异常(例如Antd组件会将未声明属性渲染为文档标签属性,输出控制台错误)。
因此往往需要做自定义属性过滤后再进行透传,过滤方式包括不限于以下这些:
- 通过拓展运算符
- 通过omit函数
改造后代码如下:
import React, { useEffect } from "react"; import { Button, ButtonProps } from "@arco-design/web-react"; type IProps = ButtonProps & { hello?: string; }; /// 这里通过拓展运算符过滤原生组件未声明属性 const MyButton: React.FC<IProps> = ({ hello, ...restProps }) => { useEffect(() => console.log(hello), [hello]); return <Button {...restProps} />; }; export default withDefaultProps<IProps>({ hello: "world", })(MyButton);
到此这篇关于React组件二次包装的具体实现的文章就介绍到这了,更多相关React组件二次包装装内容请搜索阿兔在线工具以前的文章或继续浏览下面的相关文章希望大家以后多多支持阿兔在线工具!