前言
antd
的 treeSelect
用起来没 element
的爽快,因为我们在实际使用的过程中,老感觉它少了什么东西。比如在使用 allowClear
的时候,找不到清空回调;比如在使用 showSearch
自定义搜索内容后没找到想要的结果,怎么重置 treeData
; 问题很多,官方文档都没有给出对应的解决方案,很是令人头大。然而,经过一段时间摸索后,我发现其实这也许不是 antd
的问题,只是自己对react不够熟悉的问题,嗯,一切的问题都只是因为自己太菜了。
案例
今天突发奇想,把项目中自定义的 class
版本的 treeSelect
组件用 hook
语法进行重写,会不会更好维护一些呢?那么上代码
功能点
- 1、实现树状动态加载
- 2、实现自定义查询功能
- 3、无缝对接 Form
组件代码
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { TreeSelect } from 'antd';
import { debounce } from '@/utils';
import * as API from '@/axios/api';
const propTypes = {
onChange: PropTypes.func,
value: PropTypes.string,
};
/**
* 转化节点
* @param {Array} list
* @param {Number} pid
*/
const format = (list, pid) => {
return list.map(el => {
return {
pid,
isLeaf: false,
id: el.id,
title: el.title,
value: el.id,
};
});
};
/**
* 深层递归插入子集
* @param {Array} treeData
* @param {String} pid
* @param {Array} children
*/
function deepTree(treeData, pid, children) {
treeData.forEach(item => {
if (item.id === pid) {
item.children = children;
return;
} else {
item.children && item.children.length > 0 && deepTree(item.children, pid, children);
}
});
}
/**
* 类别
* 树状下拉选择组件 *
*/
const MyTreeSelect = React.forwardRef((props, ref) => {
const [treeData, setTreeData] = useState([]);
const [treeDataCopy, setTreeDataCopy] = useState([]); // 备份数据
const [value, setValue] = useState(undefined);
// 初次请求
useEffect(() => {
getData();
}, []);
// 监听value变化
useEffect(() => {
// 如果没有选择
if (value === undefined && treeData === treeDataCopy) {
setTreeData(treeDataCopy);
}
}, [value]);
// 监听props.value变化
useEffect(() => {
if (props.value !== value) {
setValue(props.value);
}
}, [props.value]);
/**
* 请求函数 *
*/
// 根据id获取数据,id为0时取顶层数据
async function getData(pid = 0) {
let res = await API.getData({ pid });
if (res.code !== '200' || !res.data) {
return false;
}
let list = format(res.data, pid);
// 为0时初次加载;否则就是展开加载
if (pid === 0) {
setTreeData(list);
setTreeDataCopy(list);
} else {
// 展开时的加载
let treeDataList = [...treeData];
deepTree(treeDataList, pid, [...list]);
setTreeData(treeDataList);
setTreeDataCopy(treeDataList);
}
}
/**
* 功能函数 *
*/
// 展开加载数据;注意:此处的展开函数必须返回一个promise异步函数
function onLoadData(treeNode) {
const { id } = treeNode.props;
// 用return才有加载效果
return getData(id);
}
// 搜索防抖
const debounceFn = debounce(e => {
// 当输入有值时,进行搜索查询;否则重置 treeData
if (e) {
// 每次搜索时,清空已选中的选项,以防搜索结果没有选中值而出现显示bug
handleOnChange(undefined);
// 搜索结果; 此处也可以写你的异步请求
setTreeData([]);
}
}, 500);
// 搜索
function handleOnSearch(e) {
debounceFn(e);
}
// 选择选项
function handleOnChange(e) {
props.onChange && props.onChange(e);
}
// 展开下拉框
function handleOnDropdownVisibleChange(e) {
// 当关闭下拉框且 treeData 数据为空时,重置 treeData
if (!e && treeData.length === 0) {
setTreeData(treeDataCopy);
}
}
return (
<TreeSelect
showSearch
allowClear
treeDataSimpleMode
ref={ref}
value={value}
treeData={treeData}
loadData={onLoadData}
onSearch={handleOnSearch}
onChange={handleOnChange}
onDropdownVisibleChange={handleOnDropdownVisibleChange}
placeholder="请选择案件类别"
searchPlaceholder="请输入关键字"
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
/>
);
});
MyTreeSelect.propTypes = propTypes;
export default React.memo(MyTreeSelect);
使用
<FormItem label="类别">
{getFieldDecorator('type')(<MyTreeSelect />)}
</FormItem>