React+Ts實(shí)現(xiàn)二次封裝組件
前言
在react中相信大家用的最多的組件庫就是Antd了,可是往往在公司的實(shí)際開發(fā)過程中,我們會(huì)發(fā)現(xiàn)ui給的設(shè)計(jì)圖和組件有著不小的差別,不管是在樣式上還是功能上。并且可能存在根據(jù)項(xiàng)目主題定制的風(fēng)格,那我們最好是不要一個(gè)個(gè)的去使用antd里的組件,然后再去修改其內(nèi)部樣式和功能擴(kuò)展或修改,這樣不管是從效率還是維護(hù)性都不是理想的。那如何優(yōu)雅的對(duì)現(xiàn)有的組件庫進(jìn)行二次封裝就是我們今天探討的主要內(nèi)容了,以下做法有不足之處望各位大佬不吝賜教。
ps:項(xiàng)目演示采用 react18+umi4+antd5+ts
樣式
若是確定了組件在項(xiàng)目中的整體樣式,可以寫在全局樣式中,做一個(gè)覆蓋效果。
以下的overrides.less 是Umi中專門為修改組件樣式定制的,我們通過增加id選擇器的方式加強(qiáng)權(quán)重來進(jìn)行覆蓋默認(rèn)樣式的效果。
若是有某些樣式不一致的地方,我們可以直接到組件里再進(jìn)行一次覆蓋,如下所示:
同理只是又借用了組件的id進(jìn)行加強(qiáng)權(quán)重
這樣就避免了 !import的普遍存在了
類型擴(kuò)展
我們可能會(huì)遇到組件依賴外部類型來決定內(nèi)部類型的情況,就比如表格組件中列表的數(shù)據(jù)類型肯定是不一樣的,那我們就需要通過泛型讓外面?zhèn)鬟f進(jìn)來。從而達(dá)到規(guī)范的效果。
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> // 總數(shù)據(jù) tableOnChange?: onChangeType<T> // 變化回調(diào) tableRowSelection?: TableRowSelection<T> // 自定義行數(shù)據(jù)設(shè)置 tablePagination?: TablePaginationConfig //自定義分頁配置 bottomTitleFlag?: boolean // 左下頁角信息是否顯示 } // table ref的參數(shù) 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
我們使用時(shí)就可以直接傳入類型
功能擴(kuò)展 繼承 修改 攔截
我們想要擴(kuò)展組件內(nèi)的結(jié)構(gòu),可以自定義字段來控制顯示,如下的bottomTitleFlag,就是控制標(biāo)題的展示。
我們可以通過...otherProps的方式接收剩余參數(shù),從而實(shí)現(xiàn)了可以在我們的組件上傳遞antd規(guī)定的組件屬性,若是相同則會(huì)進(jìn)行覆蓋采用新傳入的。若是我們不傳遞則采用內(nèi)置的,若是內(nèi)置的還不滿足需求,我們可以在內(nèi)置里再加上剩余參數(shù)的寫法進(jìn)行補(bǔ)充如 ...tablePagination。
事件需要攔截可以內(nèi)置一個(gè)事件,然后通過調(diào)用內(nèi)置事件時(shí)進(jìn)行數(shù)據(jù)相關(guān)處理后再去調(diào)傳入的事件,這樣就實(shí)現(xiàn)攔截的效果了。
另外對(duì)于外界可能會(huì)用的的一些參數(shù)我們可以通過 useImperativeHandle 進(jìn)行Ref拋出,使得更好去獲取內(nèi)部的屬性。
const _TableDefault = <T extends object>(props: TProps<T>, ref: ForwardedRef<TableDefaultRefProps<T>>) => { const { tableData, tableOnChange, tableRowSelection, tablePagination, bottomTitleFlag = false, ...otherProps } = props // 當(dāng)前選擇的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) }, []) // 選擇多選框回調(diào) 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] ) // 結(jié)束頁碼 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
我們想要擴(kuò)展組件內(nèi)的功能,增加內(nèi)置功能可以直接在組件內(nèi)部增加,使用內(nèi)部數(shù)據(jù)來完成,最后進(jìn)行一個(gè)拋出。
這里使用 node[fieldNames.key] 計(jì)算屬性名的原因是樹組件的字段可能會(huì)發(fā)生變化,所以我們需要根據(jù)傳入的fieldNames來進(jìn)行字段更新。
樹組件
// 當(dāng)前節(jié)點(diǎn)展開 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, }))
到此這篇關(guān)于React+Ts實(shí)現(xiàn)二次封裝組件的文章就介紹到這了,更多相關(guān)React Ts封裝組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react?Scheduler?實(shí)現(xiàn)示例教程
這篇文章主要為大家介紹了react?Scheduler?實(shí)現(xiàn)示例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09React項(xiàng)目中應(yīng)用TypeScript的實(shí)現(xiàn)
TypeScript通常都會(huì)依賴于框架,例如和vue、react 這些框架結(jié)合,本文就主要介紹了React項(xiàng)目中應(yīng)用TypeScript的實(shí)現(xiàn),分享給大家,具體如下:2021-09-09React?Hook中的useEffecfa函數(shù)的使用小結(jié)
React 會(huì)在組件更新和卸載的時(shí)候執(zhí)行清除操作, 將上一次的監(jiān)聽取消掉, 只留下當(dāng)前的監(jiān)聽,這篇文章主要介紹了React?Hook?useEffecfa函數(shù)的使用細(xì)節(jié)詳解,需要的朋友可以參考下2022-11-11React團(tuán)隊(duì)測試并發(fā)特性詳解
這篇文章主要為大家介紹了React團(tuán)隊(duì)測試并發(fā)特性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08React RenderProps模式運(yùn)用過程淺析
render props是指一種在 React 組件之間使用一個(gè)值為函數(shù)的 prop 共享代碼的技術(shù)。簡單來說,給一個(gè)組件傳入一個(gè)prop,這個(gè)props是一個(gè)函數(shù),函數(shù)的作用是用來告訴這個(gè)組件需要渲染什么內(nèi)容,那么這個(gè)prop就成為render prop2023-03-03解決React報(bào)錯(cuò)Unexpected default export of an
這篇文章主要為大家介紹了React報(bào)錯(cuò)Unexpected default export of anonymous function解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12React?Native中原生實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入的示例詳解
在React?Native社區(qū)中,原生動(dòng)態(tài)導(dǎo)入一直是期待已久的功能,在這篇文章中,我們將比較靜態(tài)和動(dòng)態(tài)導(dǎo)入,學(xué)習(xí)如何原生地處理動(dòng)態(tài)導(dǎo)入,以及有效實(shí)施的最佳實(shí)踐,希望對(duì)大家有所幫助2024-02-02react結(jié)合bootstrap實(shí)現(xiàn)評(píng)論功能
這篇文章主要為大家詳細(xì)介紹了react結(jié)合bootstrap實(shí)現(xiàn)評(píng)論功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05