当前位置: 首页 > news >正文

Ant Design 结合 React 参考 Vben 逻辑实现的描述列表组件封装实践

前言

在现代前端开发中,组件化是提高代码复用性和维护性的关键。本文将介绍如何基于 Ant Design 的 Descriptions 组件,结合 React 和 TypeScript,参考 Vben Admin 的设计思想,封装一个功能完善、灵活易用的描述列表组件。

组件设计思路

需求分析

描述列表(Description List)通常用于展示对象的详细信息,如用户资料、订单详情等。一个高质量的描述列表组件需要具备以下特性:

  • 支持动态配置描述项

  • 支持自定义样式

  • 支持条件渲染

  • 提供实例方法用于动态更新

  • 完善的类型定义

技术选型

  • UI 基础:Ant Design 的 Descriptions 组件

  • 框架:React 18

  • 类型系统:TypeScript

  • 设计参考:Vben Admin 的组件封装思想

核心实现

1. 类型定义(typing.ts)

首先定义组件所需的类型,确保类型安全:

import type { ReactNode } from 'react';
import type { DescriptionsProps } from 'antd';// 描述项配置接口
export interface DescItem {labelMinWidth?: number;contentMinWidth?: number;labelStyle?: React.CSSProperties;field: string;label: ReactNode;span?: number;show?: (data: Record<string, any>) => boolean;render?: (value: any, data: Record<string, any>) => ReactNode;
}// 组件属性接口
export interface DescriptionProps extends AntDescriptionsProps {schema?: DescItem[];data?: Record<string, any>;column?: number;
}// 组件实例接口
export interface DescInstance {setDescProps: (props: Partial<DescriptionProps>) => void;
}

2. 组件封装(Description.tsx)

使用 forwardRef 转发 ref,结合 useState 管理动态 props,通过 useImperativeHandle 暴露实例方法:

 

import React, { useRef, useState, useImperativeHandle, forwardRef } from "react";
import { Descriptions } from "antd";
import type { DescriptionProps, DescInstance, DescItem } from "./typing";const Description = forwardRef<DescInstance, DescriptionProps>((props, ref) => {const [innerProps, setInnerProps] = useState<DescriptionProps>(props);const mergedProps = { ...innerProps, ...props };const {schema,data,column = 2,bordered = true,contentStyle,labelStyle,size = "default",...descriptionsProps} = mergedProps;useImperativeHandle(ref,() => ({setDescProps: (newProps) => {setInnerProps((prev) => ({ ...prev, ...newProps }));},}),[]);const renderLabel = ({ label, labelMinWidth }: DescItem) => {if (!labelMinWidth || !labelStyle) return label;return <div style={{ minWidth: `${labelMinWidth}px`, ...labelStyle }}>{label}</div>;};const renderDescriptionItem = (item: DescItem) => {const { label, field, span, show, render, contentMinWidth } = item;const value = data ? data[field] : undefined;if (show && !show(data)) return null;const getContent = () => render ? render(value, data) : value;return (<Descriptions.Itemkey={field}label={renderLabel(item)}span={span}styles={{ label: { ...labelStyle }, content: { ...contentStyle } }}>{contentMinWidth ? <div style={{ minWidth: `${contentMinWidth}px` }}>{getContent()}</div> : getContent()}</Descriptions.Item>);};return (<Descriptionscolumn={column}bordered={bordered}size={size}{...descriptionsProps}>{schema?.map((item) => renderDescriptionItem(item))}</Descriptions>);
});Description.displayName = "Description";
export default Description;

3. 自定义 Hook(useDescription.ts)

封装自定义 Hook 用于管理组件实例和状态更新:

 

import { useRef, useEffect } from 'react';
import type { MutableRefObject } from 'react';
import type { DescriptionProps, DescInstance } from './typing';export function useDescription(initialProps?: Partial<DescriptionProps>) {const descRef = useRef<DescInstance | null>(null);const isLoaded = useRef(false);const timerRef = useRef<NodeJS.Timeout | null>(null);const register = (instance: DescInstance | null) => {if (instance) {descRef.current = instance;isLoaded.current = true;if (initialProps) instance.setDescProps(initialProps);} else {descRef.current = null;isLoaded.current = false;if (timerRef.current) {clearTimeout(timerRef.current);timerRef.current = null;}}};const setDescProps = (props: Partial<DescriptionProps>) => {if (!isLoaded.current) return;if (descRef.current) {descRef.current.setDescProps(props);} else if (!timerRef.current) {timerRef.current = setTimeout(() => {setDescProps(props);timerRef.current = null;}, 100);}};useEffect(() => {return () => {if (timerRef.current) clearTimeout(timerRef.current);isLoaded.current = false;descRef.current = null;};}, []);return {register,setDescProps,descRef: descRef as MutableRefObject<DescInstance>};
}

4. 组件导出(index.ts)

规范组件导出:

import Description from './src/Description.tsx';
​export {  Description  }

功能特性

  1. 动态配置:通过 schema 定义描述项,支持自定义标签、内容、样式

  2. 数据驱动:通过 data 属性动态渲染内容

  3. 条件渲染:支持 show 函数控制描述项显示/隐藏

  4. 自定义渲染:支持 render 函数自定义内容展示

  5. 实例方法:通过 useDescription hook 获取实例,调用 setDescProps 动态更新

  6. 类型安全:完善的 TypeScript 类型定义

使用示例

import { Description } from './components/Descrption';
import { useDescription } from './components/Descrption/src/useDescription';const App = () => {const { register } = useDescription({schema: detailSchema,data,title: "基本信息",column: 2,labelStyle: { minWidth: "250px" },contentStyle: { minWidth: "300px" },});const schema = [{ label: '姓名', field: 'name', labelMinWidth: 100 },{ label: '年龄', field: 'age' },{ label: '邮箱', field: 'email', render: (value) => <a href={`mailto:${value}`}>{value}</a> },{ label: '地址', field: 'address', span: 2, show: (data) => data.address },];const data = {name: '张三',age: 28,email: 'zhangsan@example.com',address: '北京市海淀区'};return (<Descriptionref={register}/>);
};

总结

本文介绍了如何基于 Ant Design 和 React,参考 Vben Admin 的设计思想,封装一个功能完善的描述列表组件。通过 TypeScript 类型定义确保类型安全,使用 React hooks 管理状态和实例,支持动态配置和自定义渲染,满足各种复杂场景的需求。这种组件封装方式不仅提高了代码复用性,也便于维护和扩展,是现代前端开发中的最佳实践之一。

http://www.lryc.cn/news/599519.html

相关文章:

  • C#模拟pacs系统接收并解析影像设备数据(DICOM文件解析)
  • 【unitrix】 6.16 非负整数类型( TUnsigned )特质(t_unsingned.rs)
  • Docker镜像导入解析:docker import vs docker load
  • 2025最新蜘蛛池在百度SEO中的应用
  • vue2+node+express+MongoDB项目安装启动启动
  • ELK Stack技术栈
  • 前端基础知识Vue系列 - 27(Vue项目中如何解决跨域)
  • 养老服务行业怎么解决采购管理难题?
  • 配置Mac/Linux终端启动执行脚本
  • 数据赋能(332)——安全与合规——保密管理
  • 代码随想录day45dp12
  • 读书笔记8:供应链思维下的企业战略与数字生态
  • OpenCV(04)梯度处理,边缘检测,绘制轮廓,凸包特征检测,轮廓特征查找
  • MCP协议详细教程
  • The Magic Mask for Android:解锁无限可能的安卓自定义套件
  • 【面试场景题】外卖点餐系统设计思路
  • [MMU]四级页表查找(table walk)的流程
  • SQL性能优化
  • 【LeetCode Solutions】LeetCode 热题 100 题解(16 ~ 20)
  • 系统编程——文件IO
  • SpringBoot整合Fastexcel/EasyExcel导出Excel导出多个图片
  • 面向对象编程实战:Python打造你的数码宠物世界
  • Java NIO FileChannel在大文件传输中的性能优化实践指南
  • 盟接之桥说制造:构建以预防为核心的供应链客诉管理体系
  • GitHub git push 推送大文件
  • 【第四章:大模型(LLM)】01.Embedding is all you need-(6)从 Word2Vec 到推荐/广告系统,再到大语言模型(LLM)
  • Three.js 控制器和交互设计:OrbitControls + Raycaster 实战
  • ✨ 使用 Flask 实现头像文件上传与加载功能
  • Kafka——多线程开发消费者实例
  • MCP工具开发实战:打造智能体的“超能力“