react+antd 递归实现树状目录操作


Posted in Javascript onNovember 02, 2020

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 => {
 let treeArr = [];
 //获取顶级父元素集合
 let roots = data.filter(elemt => elemt.parentId === 0);
 treeArr.push(...roots);
 //从顶级元素开始,获取每个元素的子元素放到该元素的children属性中
 const getChildren = (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则先套上<TreeNode>,再对他children中的元素做相同的操纵,直到children为空的元素停止,说明他们已经是最深的那一层了。 
  if(value.children){
   return(
    <TreeNode title={
     <span>
     {value.name}
     <Icon type="plus" onClick={this.showModal.bind(this,2,value.id)} />
     <Icon type="edit" onClick={this.showModal.bind(this,1,value.id)} />
     <Icon type="delete" onClick={this.showModal.bind(this,0,value.id)} />
     </span>
    } key={value.id}>
     //对children中的每个元素进行递归
     {this.renderTree(value.children)} 
    </TreeNode>  
   )
  }  
 })

至此,就基本完成对数据的格式和对UI树的格式啦,最后在树控件中调用它,OK~

5.疯狂输出

render(){
 return(
 <Tree showLine={true}>
 {this.renderTree(this.state.treeArr)}
 </Tree>
 )
 }

运行,Bingo~

tips

因为目录树的每一项都是可编辑的,而原始的UI组件也没有可用配置,后来查阅文档竟然应该在TreeNode的title树形中添加树的自定义元素,可以,很强势,官方文档,看就完了,哈哈。

补充知识:antd的tree树形组件异步加载数据小案例

前不久,做业务需求时遇到一个树形选择的 问题 , 子节点的数据要通过点击展开才去加载,在antd给出的官方文档中也有关于动态加载数据的demo,然而不够详细,自己研究之后,整理出来共享给大家借鉴下。

view.jsx(纯函数式声明)

// 渲染树的方法

const loop = data => data.map((item) => {
  const index = item.regionName.indexOf(modelObj.searchValue);
  const beforeStr = item.regionName.substr(0, index);
  const afterStr = item.regionName.substr(index + modelObj.searchValue.length);
  const title = index > -1 ? (
  <span>
   {beforeStr}
   <span style={{ color: '#f50' }}>{modelObj.searchValue}</span>
   {afterStr}
  </span>
  ) : <span>{item.regionName}</span>;
  if (item.children && item.children.length > 0) {
  return (
   <TreeNode key={item.regionCode} parentId={item.parentId} title={getTreeTitle(title, item)}>
   {loop(item.children)}
   </TreeNode>
  );
  }
  return <TreeNode key={item.regionCode} parentId={item.parentId} title={getTreeTitle(title, item)} isGetDealer={item.isGetDealer} isLeaf={item.isLeaf}/>; 
  });

//树结构展开触发的方法

const onExpand = (expandedKeys) => {
  console.log('onExpand-->', expandedKeys);
  
  dispatch({
  type: `${namespace}/onExpand`,
  payload: {
   expandedKeys,
   autoExpandParent: false,
  }
  });
 }

//异步加载树形结构子节点的方法

const onLoadData = (treeNode) => {
  return new Promise((resolve) => {
  resolve();
  if(treeNode.props.isGetDealer===1){
  let cityIds =treeNode.props.eventKey;
  
  dispatch({
   type: `${namespace}/queryDealers`,
   payload: {
   checkedKeys:cityIds,
   }
  });
  }
 })
 }

//节点被选中时触发的方法

const onCheck = (checkedKeys,e) => {
 console.log('checkedKeys',checkedKeys);
 
 if(checkedKeys.length > 0) {
  updateModel(checkedKeys,'checkedKeys');
 } else {
  updateModel([], 'sellers');
  updateModel([], 'checkedKeys');
 }
 }

//dom渲染

return (
 {/* 经销商省市选择 */}
     {
     modelObj.designatSeller === 1 && <div style={{marginBottom:20}}>
      <div className = {styles.treeStyle} style={{ backgroundColor: '#FBFBFB', width: 343, paddingLeft: 8, height: 200, overflow: 'auto' }}>
      <Tree
       checkable
       onExpand={onExpand}
       expandedKeys={modelObj.expandedKeys}
       checkedKeys={modelObj.checkedKeys}
       //checkStrictly={true}
       autoExpandParent={modelObj.autoExpandParent}
       onCheck={onCheck}
       loadData={onLoadData}
      >
       {loop(modelObj.countryList)}
      </Tree>
      </div>
     </div>
     }
)

mod.js

//初始默认状态

const defaultState = {
 countryList:[], //省市树
 expandedKeys: [], //树数据
 autoExpandParent: true, 
 checkedKeys:[],//当前选中的节点
}

// 方法列表

effects: {
  //根据城市获取经销商
 *queryDealers({payload}, { call, put, select }) {
  let {countryList} = yield select(e => e[tmpModule.namespace]);

  let {data, resultCode } = yield call(queryDealers, {cityCodes:payload.checkedKeys});

  if(resultCode === 0 && data.length > 0) {

  let sellers = data.map(x=>x.dealerId);

  yield put({
   type: 'store',
   payload: {
   sellers
   }
  });

  let gdata = groupBy(data, 'cityCode');

  setgData(countryList);

  yield put({
   type: 'store',
   payload: {countryList } 
  });

  function setgData(arr) {
   if(arr&&arr.length>0){
   arr.forEach(x=>{
    if(x.regionCode.split(',')[1] in gdata) {
    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} = yield select(e => e[tmpModule.namespace]);
  let resp = yield call(queryCountry);
  // console.log('resp_country-->',resp)
  if(resp.resultCode === 0){
   let dataList = 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 = []; 
    })
    })
   })
   })
   yield put({
   type: 'store',
   payload: {countryList: dataList } 
   });
  }
 },
 //展开节点触发
  *onExpand({ payload }, { call, put, select }){
  yield put({
   type: 'store',
   payload: {
   expandedKeys:payload.expandedKeys,
   autoExpandParent:payload.autoExpandParent
   }
  });
  },
 }

回显时后端需要返回的数据格式如下:

checkedKeys:[0:"500000,500100,2010093000863693"

1:"500000,500100,2010093000863790"]

说明: 这个例子只是贴出了一部分重要代码,具体使用时需要补充完整。

以上这篇react+antd 递归实现树状目录操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Sample script that deletes a SQL Server database
Jun 16 Javascript
javascript 出生日期和身份证判断大全
Nov 13 Javascript
js 禁止选择功能实现代码(兼容IE/Firefox)
Apr 23 Javascript
jQuery ajax dataType值为text json探索分享
Sep 23 Javascript
谈谈JavaScript异步函数发展历程
Sep 29 Javascript
跟我学习javascript的undefined与null
Nov 17 Javascript
前端html中jQuery实现对文本的搜索功能并把搜索相关内容显示出来
Nov 14 jQuery
JavaScript ES6中的简写语法总结与使用技巧
Dec 30 Javascript
AngularJS实现的鼠标拖动画矩形框示例【可兼容IE8】
May 17 Javascript
vue中v-show和v-if的异同及v-show用法
Jun 06 Javascript
js神秘的电报密码 哈弗曼编码实现
Sep 10 Javascript
关于Vue Router的10条高级技巧总结
May 06 Vue.js
antd form表单数据回显操作
Nov 02 #Javascript
antd Select下拉菜单动态添加option里的内容操作
Nov 02 #Javascript
Vue中使用Echarts仪表盘展示实时数据的实现
Nov 01 #Javascript
JavaScript实现刮刮乐效果
Nov 01 #Javascript
微信小程序实现单个或多个倒计时功能
Nov 01 #Javascript
微信小程序实现页面监听自定义组件的触发事件
Nov 01 #Javascript
uniapp微信小程序实现一个页面多个倒计时
Nov 01 #Javascript
You might like
PHP 强制性文件下载功能的函数代码(任意文件格式)
2010/05/26 PHP
php进行支付宝开发中return_url和notify_url的区别分析
2014/12/22 PHP
Ajax::prototype 源码解读
2007/01/22 Javascript
50款非常棒的 jQuery 插件分享
2012/03/29 Javascript
Javascript算符的优先级介绍
2013/03/20 Javascript
通过javascript获取iframe里的值示例代码
2013/06/24 Javascript
javascript中数组array及string的方法总结
2014/11/28 Javascript
使用jquery插件qrcode生成二维码
2015/10/22 Javascript
javascript:void(0)点击登录没反应怎么解决
2015/11/13 Javascript
AngularJS Module方法详解
2015/12/08 Javascript
jquery实现简单的遮罩层
2016/01/08 Javascript
高效利用Angular中内置服务$http、$location等
2016/03/22 Javascript
浅析javascript异步执行函数导致的变量变化问题解决思路
2016/05/13 Javascript
js生成随机数方法和实例
2017/01/17 Javascript
angular项目中bootstrap-datetimepicker时间插件的使用示例
2018/03/15 Javascript
详解vue项目接入微信JSSDK的坑
2018/12/14 Javascript
vue router带参数页面刷新或回退参数消失的解决方法
2019/02/27 Javascript
JavaScript中常用的简洁高级技巧总结
2019/03/10 Javascript
深入解析Vue源码实例挂载与编译流程实现思路详解
2019/05/05 Javascript
Element的el-tree控件后台数据结构的生成以及方法的抽取
2020/03/05 Javascript
[50:27]OG vs LGD 2018国际邀请赛淘汰赛BO3 第一场 8.26
2018/08/30 DOTA
利用python画一颗心的方法示例
2017/01/31 Python
一个基于flask的web应用诞生 用户注册功能开发(5)
2017/04/11 Python
python实现简易动态时钟
2018/11/19 Python
Python实现E-Mail收集插件实例教程
2019/02/06 Python
使用Pyhton 分析酒店针孔摄像头
2020/03/04 Python
Cpython解释器中的GIL全局解释器锁
2020/11/09 Python
Willer台湾:日本高速巴士/夜行巴士预约
2017/07/09 全球购物
Spartoo西班牙官网:法国时尚购物网站
2018/03/27 全球购物
超市促销实习自我鉴定
2013/09/23 职场文书
中学教师管理制度
2014/01/14 职场文书
高中生自我评价范文2015
2015/03/03 职场文书
linux下安装redis图文详细步骤
2021/12/04 Redis
浅谈GO中的Channel以及死锁的造成
2022/03/18 Golang
MySQL分区路径子分区再分区
2022/04/13 MySQL
如何开启Apache,Nginx和IIS服务器的GZIP压缩功能
2022/04/29 Servers