需求背景:使用折叠面板的形式展示数据,面板内部数据需要在打开时请求接口获取。
遇到问题:最开始使用Antd 的折叠面板组件,它对于数据直接渲染是没问题的,但是不好满足打开面板时再动态加载数据的需求,于是自己手写了一个。
效果展示(已脱敏):
话不多说,以下是手写的组件代码:
面板的header 内容结构可以根据自己需要调整
/* eslint-disable @typescript-eslint/no-unused-vars */
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Col, Row } from 'antd';
import { useRef, useState } from 'react';
import './style.less';
// 内容
import TableList from './TableList';
/** ===================================
* @names: 自定义折叠面板业务组件
* @description:
* @author:
* @date: 2024-06-20
*======================================*/
export default function CollapseList(props: any) {
const [active, setActive] = useState(false);
const tableRef = useRef<any>();
return (
<div className="collapse-item">
<div className="collapse-item-header">
<Row>
<Col span={12}>
<div>test</div>
</Col>
<Col span={8}>
<div>2022-06-20</div>
</Col>
<div className="expand">
{!active ? (
<div
className="expandIconCpe"
onClick={() => {
setActive(!active);
// 点击展开时调用子组件加载数据的方法请求
tableRef.current?.handleGetData();
}}
>
展开
<RightOutlined />
</div>
) : (
<div
className="expandIconCpe"
onClick={() => {
setActive(!active);
}}
>
收起
<DownOutlined />
</div>
)}
</div>
</Row>
</div>
<div
className={
active ? 'collapse-item-content-active' : 'collapse-item-content-hide'
}
>
{/* 面板内容,可以自定义,我这里是封装了一个表格组件 用ref绑定 */}
<TableList ref={tableRef} />
</div>
</div>
);
}
样式css:
.collapse-item {
border-bottom: 1px solid rgba(5, 5, 5, 6%);
border-radius: unset;
.collapse-item-header {
position: relative;
padding: 12px 16px;
color: rgba(0, 0, 0, 88%);
cursor: pointer;
.expand {
font-size: 12px;
position: absolute;
right: 16px;
}
.expandIconCpe {
color: #0065ff !important;
}
}
.collapse-item-content-hide {
display: none;
}
.collapse-item-content-active {
display: block;
background-color: #fff;
}
.ant-table-tbody > tr:last-child > td {
border-bottom: none;
}
.ant-row {
flex-wrap: nowrap;
word-break: break-all;
}
}
面板内容组件中写接口请求,使用 forwardRef
+ useImperativeHandle
让父组件调用子组件的方法。
核心代码:
import { forwardRef, useImperativeHandle} from 'react';
interface TableRef {
handleGetData: (params: any) => void;
}
const TableList = forwardRef<TableRef>((props, ref) => {
// 子组件中
useImperativeHandle(ref, () => ({
// handleGetData是暴露给父组件的方法
handleGetData: (params: any) => {
// 这里可以自定义封装接口请求,然后渲染数据
},
}));
})
export default TableList;
Tips:
为了提高性能,不让每次点击展开都去发起请求,可以在请求成功一次结果后,设置一个标记,下次再点击展开时,判断如果已经标记请求成功过了,就不发起请求,展示上一次结果。