文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

React+Ts实现二次封装组件

2023-05-17 05:22

关注

前言

在react中相信大家用的最多的组件库就是Antd了,可是往往在公司的实际开发过程中,我们会发现ui给的设计图和组件有着不小的差别,不管是在样式上还是功能上。并且可能存在根据项目主题定制的风格,那我们最好是不要一个个的去使用antd里的组件,然后再去修改其内部样式和功能扩展或修改,这样不管是从效率还是维护性都不是理想的。那如何优雅的对现有的组件库进行二次封装就是我们今天探讨的主要内容了,以下做法有不足之处望各位大佬不吝赐教。

ps:项目演示采用 react18+umi4+antd5+ts

样式

若是确定了组件在项目中的整体样式,可以写在全局样式中,做一个覆盖效果。
以下的overrides.less 是Umi中专门为修改组件样式定制的,我们通过增加id选择器的方式加强权重来进行覆盖默认样式的效果。

image.png

若是有某些样式不一致的地方,我们可以直接到组件里再进行一次覆盖,如下所示:
同理只是又借用了组件的id进行加强权重

image.png

这样就避免了 !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

我们使用时就可以直接传入类型

image.png

功能扩展 继承 修改 拦截

我们想要扩展组件内的结构,可以自定义字段来控制显示,如下的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封装组件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯