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 相关文章推荐
使用jQuery轻松实现Ajax的实例代码
Aug 16 Javascript
JavaScript(JS) 压缩 / 混淆 / 格式化 批处理工具
Dec 10 Javascript
web的各种前端打印方法之jquery打印插件jqprint实现网页打印
Jan 09 Javascript
js中typeof的用法汇总
Dec 12 Javascript
node.js中的path.dirname方法使用说明
Dec 09 Javascript
探讨JavaScript中的Rest参数和参数默认值
Jul 29 Javascript
简单实现js选项卡切换效果
Feb 03 Javascript
javascript函数的四种调用模式
Jan 08 Javascript
详解使用fetch发送post请求时的参数处理
Apr 05 Javascript
Vue2.0实现购物车功能
Jun 05 Javascript
VUE中使用HTTP库Axios方法详解
Feb 05 Javascript
深入理解 ES6中的 Reflect用法
Jul 18 Javascript
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上传文件时文件过大$_FILES为空的解决方法
2013/11/26 PHP
php下Memcached入门实例解析
2015/01/05 PHP
php校验表单检测字段是否为空的方法
2015/03/20 PHP
PHP中调用C/C++制作的动态链接库的教程
2016/03/10 PHP
php快速导入大量数据的实例方法
2019/09/23 PHP
jQuery toggle()设置CSS样式
2009/11/05 Javascript
Js与下拉列表处理问题解决
2014/02/13 Javascript
Firefox中使用outerHTML的2种解决方法
2014/06/07 Javascript
Egret引擎开发指南之视觉编程
2014/09/03 Javascript
node.js中的path.normalize方法使用说明
2014/12/08 Javascript
限制上传文件大小和格式的jQuery插件实例
2015/01/24 Javascript
js实现超酷的照片墙展示效果图附源码下载
2015/10/08 Javascript
javascript显示系统当前时间代码
2016/12/29 Javascript
Vue2.0结合webuploader实现文件分片上传功能
2018/03/09 Javascript
Vue.js上传图片到阿里云OSS存储的方法示例
2018/12/13 Javascript
Vue 子组件与数据传递问题及注意事项
2019/07/11 Javascript
element-ui 文件上传修改文件名的方法示例
2019/11/05 Javascript
微信小程序关键字变色实现代码实例
2019/12/13 Javascript
[50:05]VGJ.S vs OG 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
零基础写python爬虫之urllib2使用指南
2014/11/05 Python
Python实现的数据结构与算法之链表详解
2015/04/22 Python
Python函数可变参数定义及其参数传递方式实例详解
2015/05/25 Python
Python黑魔法Descriptor描述符的实例解析
2016/06/02 Python
Django中cookie的基本使用方法示例
2018/02/03 Python
在CMD命令行中运行python脚本的方法
2018/05/12 Python
pymongo中group by的操作方法教程
2019/03/22 Python
python opencv进行图像拼接
2020/03/27 Python
python类共享变量操作
2020/09/03 Python
Python 调用 ES、Solr、Phoenix的示例代码
2020/11/23 Python
python利用opencv实现颜色检测
2021/02/23 Python
Expedia意大利旅游网站:酒店、机票和租车预订
2017/10/30 全球购物
Lululemon英国官网:加拿大瑜伽服装品牌
2019/01/14 全球购物
巴黎欧莱雅法国官网:L’Oreal Paris
2019/04/30 全球购物
八年级生物教学反思
2014/01/22 职场文书
个人廉政承诺书
2015/04/28 职场文书
高中物理教学反思
2016/02/19 职场文书