react+antd 递归实现树状目录操作
1.写在前面
作为前端小白的我一直对算法和数据结构浅尝辄止,哝,吃亏了。使用多次递归实现数据格式化后将数据进行树状展示的目的,分享一下我这次挠头的经历~
2.数据
后台传过来的数据大概是这样的
{
"data":[
{
"id":1,
"name":"一级节点",
"parentId":0,
"isValid":true,
"canAddChild":true,
"parent":null,
"children":[]
},{
"id":3,
"name":"二级节点",
"parentId":1,
"isValid":true,
"canAddChild":true,
"parent":null,
"children":[]
}
],
"status":1
}
3.数据格式
data里面每个元素的parentId指向是父级元素的id,parentId为0的是结构树的顶级元素,但现在是个平面的数组不好处理,而我们要做的是树状的结构,所以首先要对数据进行格式化,将一个元素的所有子元素放到该元素的children属性中去。那么,递归就来了。
createTree=data=>{
lettreeArr=[];
//获取顶级父元素集合
letroots=data.filter(elemt=>elemt.parentId===0);
treeArr.push(...roots);
//从顶级元素开始,获取每个元素的子元素放到该元素的children属性中
constgetChildren=(resultarr,data)=>{
resultarr.forEach((elemt,index)=>{
elemt.children=data.filter((item,index)=>item.parentId===elemt.id);
//判断当前元素是不是有子元素被添加,如果有,再在子元素这一层循环
if(elemt.children.length>0){
getChildren(elemt.children,data);
}
});
}
getChildren(treeArr,data);
//最后更新一下数据
this.setState({
treeArr
})
4.组件格式
因为UI组件用的是antd,使用Tree和TreeNode做树结构。因为数据已经是树状的了,而且深度我们不确定,想要按层级输出UI控件,那么,递归又来了。
renderTree=jsonTree=>jsonTree.map(value=>{
//遍历树状数组,如果发现他有children则先套上,再对他children中的元素做相同的操纵,直到children为空的元素停止,说明他们已经是最深的那一层了。
if(value.children){
return(
{value.name}
}key={value.id}>
//对children中的每个元素进行递归
{this.renderTree(value.children)}
)
}
})
至此,就基本完成对数据的格式和对UI树的格式啦,最后在树控件中调用它,OK~
5.疯狂输出
render(){
return(
{this.renderTree(this.state.treeArr)}
)
}
运行,Bingo~
tips
因为目录树的每一项都是可编辑的,而原始的UI组件也没有可用配置,后来查阅文档竟然应该在TreeNode的title树形中添加树的自定义元素,可以,很强势,官方文档,看就完了,哈哈。
补充知识:antd的tree树形组件异步加载数据小案例
前不久,做业务需求时遇到一个树形选择的问题,子节点的数据要通过点击展开才去加载,在antd给出的官方文档中也有关于动态加载数据的demo,然而不够详细,自己研究之后,整理出来共享给大家借鉴下。
view.jsx(纯函数式声明)
//渲染树的方法
constloop=data=>data.map((item)=>{
constindex=item.regionName.indexOf(modelObj.searchValue);
constbeforeStr=item.regionName.substr(0,index);
constafterStr=item.regionName.substr(index+modelObj.searchValue.length);
consttitle=index>-1?(
{beforeStr}
{modelObj.searchValue}
{afterStr}
):{item.regionName};
if(item.children&&item.children.length>0){
return(
{loop(item.children)}
);
}
return ;
});
//树结构展开触发的方法
constonExpand=(expandedKeys)=>{
console.log('onExpand-->',expandedKeys);
dispatch({
type:`${namespace}/onExpand`,
payload:{
expandedKeys,
autoExpandParent:false,
}
});
}
//异步加载树形结构子节点的方法
constonLoadData=(treeNode)=>{
returnnewPromise((resolve)=>{
resolve();
if(treeNode.props.isGetDealer===1){
letcityIds=treeNode.props.eventKey;
dispatch({
type:`${namespace}/queryDealers`,
payload:{
checkedKeys:cityIds,
}
});
}
})
}
//节点被选中时触发的方法
constonCheck=(checkedKeys,e)=>{
console.log('checkedKeys',checkedKeys);
if(checkedKeys.length>0){
updateModel(checkedKeys,'checkedKeys');
}else{
updateModel([],'sellers');
updateModel([],'checkedKeys');
}
}
//dom渲染
return(
{/*经销商省市选择*/}
{
modelObj.designatSeller===1&&
{loop(modelObj.countryList)}
mod.js
//初始默认状态
constdefaultState={
countryList:[],//省市树
expandedKeys:[],//树数据
autoExpandParent:true,
checkedKeys:[],//当前选中的节点
}
//方法列表
effects:{
//根据城市获取经销商
*queryDealers({payload},{call,put,select}){
let{countryList}=yieldselect(e=>e[tmpModule.namespace]);
let{data,resultCode}=yieldcall(queryDealers,{cityCodes:payload.checkedKeys});
if(resultCode===0&&data.length>0){
letsellers=data.map(x=>x.dealerId);
yieldput({
type:'store',
payload:{
sellers
}
});
letgdata=groupBy(data,'cityCode');
setgData(countryList);
yieldput({
type:'store',
payload:{countryList}
});
functionsetgData(arr){
if(arr&&arr.length>0){
arr.forEach(x=>{
if(x.regionCode.split(',')[1]ingdata){
x.children=gdata[x.regionCode.split(',')[1]].map(x=>{
return{
children:[],
regionName:x.dealerName,
regionCode:x.provinceCode+','+x.cityCode+','+x.dealerId,
regionId:x.dealerId,
isLeaf:true
};
})
}else{
setgData(x.children);
}
})
}
}
}
},
//获取省市树
*queryCountry({},{call,put,select}){
let{countryList,biz}=yieldselect(e=>e[tmpModule.namespace]);
letresp=yieldcall(queryCountry);
//console.log('resp_country-->',resp)
if(resp.resultCode===0){
letdataList=cloneDeep(countryList);
dataList=[{
children:resp.data,
regionName:'全国',
regionId:'100000',
regionCode:'100000'}];
dataList.map((one,first)=>{
one.children.map((two,second)=>{
two.children.map((three,third)=>{
three.children.map((four,fouth)=>{
four.regionCode=three.regionCode+','+four.regionCode;
//是否为最后的子节点去获取经销商
four.isGetDealer=1;
four.children=[];
})
})
})
})
yieldput({
type:'store',
payload:{countryList:dataList}
});
}
},
//展开节点触发
*onExpand({payload},{call,put,select}){
yieldput({
type:'store',
payload:{
expandedKeys:payload.expandedKeys,
autoExpandParent:payload.autoExpandParent
}
});
},
}
回显时后端需要返回的数据格式如下:
checkedKeys:[0:"500000,500100,2010093000863693"
1:"500000,500100,2010093000863790"]
说明:这个例子只是贴出了一部分重要代码,具体使用时需要补充完整。
以上这篇react+antd递归实现树状目录操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。