怎么使用React+Ts实现二次封装组件
这篇文章主要介绍了怎么使用React+Ts实现二次封装组件的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么使用React+Ts实现二次封装组件文章都会有所收获,下面我们一起来看看吧。
样式
若是确定了组件在项目中的整体样式,可以写在全局样式中,做一个覆盖效果。
以下的overrides.less 是Umi中专门为修改组件样式定制的,我们通过增加id选择器的方式加强权重来进行覆盖默认样式的效果。
若是有某些样式不一致的地方,我们可以直接到组件里再进行一次覆盖,如下所示:
同理只是又借用了组件的id进行加强权重
这样就避免了 !import的普遍存在了
类型扩展
我们可能会遇到组件依赖外部类型来决定内部类型的情况,就比如表格组件中列表的数据类型肯定是不一样的,那我们就需要通过泛型让外面传递进来。从而达到规范的效果。
import type { PaginationProps } from 'antd' import { Table } from 'antd' import type { TableProps } from 'antd/es/table' import { FilterValue, RowSelectMethod, SorterResult, TableCurrentDataSource, TablePaginationConfig, TableRowSelection, } from 'antd/es/table/interface' import { ForwardedRef, forwardRef, Key, useCallback, useImperativeHandle, useMemo, useState } from 'react' import styles from './index.less' // 处理forwardRef使其可以接受泛型 declare module 'react' { // eslint-disable-next-line @typescript-eslint/ban-types function forwardRef<T, P = {}>( render: (props: P, ref: React.Ref<T>) => React.ReactElement | null ): (props: P & React.RefAttributes<T>) => React.ReactElement | null } export type onChangeType<RecordType> = ( pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<RecordType> | SorterResult<RecordType>[], extra: TableCurrentDataSource<RecordType> ) => void export type onSelectChangeType<T> = ( selectedRowKeys: Key[], selectedRows: T[], info: { type: RowSelectMethod } ) => void interface TProps<T> extends React.PropsWithChildren<TableProps<T>> { tableData?: pagingResProps<T> // 总数据 tableOnChange?: onChangeType<T> // 变化回调 tableRowSelection?: TableRowSelection<T> // 自定义行数据设置 tablePagination?: TablePaginationConfig //自定义分页配置 bottomTitleFlag?: boolean // 左下页角信息是否显示 } // table ref的参数 export interface TableDefaultRefProps<T> { selectedRowKeys: Key[] selectedRowRows: T[] } const _TableDefault = <T extends object>(props: TProps<T>, ref: ForwardedRef<TableDefaultRefProps<T>>) => { return <div>table</div> } const TableDefault = forwardRef(_TableDefault) export default TableDefault
我们使用时就可以直接传入类型
功能扩展 继承 修改 拦截
我们想要扩展组件内的结构,可以自定义字段来控制显示,如下的bottomTitleFlag,就是控制标题的展示。
我们可以通过...otherProps的方式接收剩余参数,从而实现了可以在我们的组件上传递antd规定的组件属性,若是相同则会进行覆盖采用新传入的。若是我们不传递则采用内置的,若是内置的还不满足需求,我们可以在内置里再加上剩余参数的写法进行补充如 ...tablePagination。
事件需要拦截可以内置一个事件,然后通过调用内置事件时进行数据相关处理后再去调传入的事件,这样就实现拦截的效果了。
另外对于外界可能会用的的一些参数我们可以通过 useImperativeHandle 进行Ref抛出,使得更好去获取内部的属性。
const _TableDefault = <T extends object>(props: TProps<T>, ref: ForwardedRef<TableDefaultRefProps<T>>) => { const { tableData, tableOnChange, tableRowSelection, tablePagination, bottomTitleFlag = false, ...otherProps } = props // 当前选择的key和行 const [selectedRowKeys, setSelectedRowKeys] = useState<Key[]>([]) const [selectedRowRows, setSelectedRowRows] = useState<T[]>([]) // 左右分页样式处理 const itemRender: PaginationProps['itemRender'] = (_, type, originalElement) => { if (type === 'prev') { return <a style={{ color: '#CCF2FF', fontSize: '14px' }}>上一页</a> } if (type === 'next') { return <a style={{ color: '#CCF2FF', fontSize: '14px' }}>下一页</a> } return originalElement } const handelChange: onChangeType<T> = useCallback((pagination, filters, sorter, extra) => { // 想要做的拦截操作 props.tableOnChange?.(pagination, filters, sorter, extra) }, []) // 选择多选框回调 const onSelectChange: onSelectChangeType<T> = (selectedRowKeys, selectedRows, info) => { setSelectedRowKeys(selectedRowKeys) setSelectedRowRows(selectedRows) } // ref抛出变量 useImperativeHandle(ref, () => ({ selectedRowKeys, selectedRowRows, })) // 开始页码 const startCode = useMemo( () => () => { if (!tableData || !tableData?.current) return 1 return (tableData?.current - 1) * tableData?.size + 1 }, [tableData] ) // 结束页码 const endCode = useMemo( () => () => { if (!tableData) return 99 return Math.min(tableData?.total, tableData?.current * tableData?.size) }, [tableData] ) return ( <div className={styles.tableDefault} id="tableDefault" > <Table rowKey="id" dataSource={tableData?.records} pagination={{ itemRender, total: tableData?.total, showSizeChanger: false, pageSize: tableData?.size, current: tableData?.current, ...tablePagination, }} onChange={handelChange} rowSelection={{ type: 'checkbox', fixed: false, columnWidth: '120px', selectedRowKeys, onChange: onSelectChange, ...tableRowSelection, }} {...otherProps} /> <div className={styles.leftIcon}></div> {bottomTitleFlag && ( <div className={styles.bottomTitle}>{`显示第${startCode()}到第${endCode()}条记录,总共${ tableData?.total }条记录`}</div> )} </div> ) } const TableDefault = forwardRef(_TableDefault) export default TableDefault
我们想要扩展组件内的功能,增加内置功能可以直接在组件内部增加,使用内部数据来完成,最后进行一个抛出。
这里使用 node[fieldNames.key] 计算属性名的原因是树组件的字段可能会发生变化,所以我们需要根据传入的fieldNames来进行字段更新。
树组件
// 当前节点展开 function nowNodeExpand(node: DataNode) { const newExpandedKeys: any[] = [] const fn = (node: any) => { node[fieldNames.key] && newExpandedKeys.push(node[fieldNames.key]) node[fieldNames.children] && node[fieldNames.children].forEach((item: DataNode) => fn(item)) } fn(node) setExpandedKeys(uniq([...expandedKeys, ...newExpandedKeys])) } // ref抛出变量 useImperativeHandle(ref, () => ({ nowNodeExpand, }))
关于“怎么使用React+Ts实现二次封装组件”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“怎么使用React+Ts实现二次封装组件”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注蜗牛博客行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:niceseo99@gmail.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
评论