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实现的移入页面上空文本框时,让它变为焦点,移出清除焦点
Jul 26 Javascript
js获取网页高度(详细整理)
Dec 28 Javascript
JQuery中对Select的option项的添加、删除、取值
Aug 25 Javascript
ajax请求乱码的解决方法(中文乱码)
Apr 10 Javascript
node.js中的fs.ftruncate方法使用说明
Dec 15 Javascript
简介JavaScript中Math.LOG10E属性的使用
Jun 14 Javascript
完美JQuery图片切换效果的简单实现
Jul 21 Javascript
详解如何使用webpack在vue项目中写jsx语法
Nov 08 Javascript
微信小程序HTTP接口请求封装的实现
Feb 21 Javascript
深入学习TypeScript 、React、 Redux和Ant-Design的最佳实践
Jun 17 Javascript
Vue父子传递实例讲解
Feb 14 Javascript
JavaScript onclick事件使用方法详解
May 15 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+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
2006/12/16 PHP
PHP中的事务使用实例
2015/05/26 PHP
PHP滚动日志的代码实现
2015/06/10 PHP
如何使用PHP对网站验证码进行破解
2015/09/17 PHP
Android App中DrawerLayout抽屉效果的菜单编写实例
2016/03/21 PHP
PHP实现的常规正则验证helper公共类完整实例
2017/04/27 PHP
Laravel如何创建服务器提供者实例代码
2019/04/15 PHP
学习ExtJS border布局
2009/10/08 Javascript
javascript中IE浏览器不支持NEW DATE()带参数的解决方法
2012/03/01 Javascript
js实现图片轮换效果代码
2013/04/16 Javascript
面向对象继承实例(a如何继承b问题)(自写)
2013/07/01 Javascript
JavaScript中数据结构与算法(三):链表
2015/06/19 Javascript
JS+CSS实现精美的二级导航效果代码
2015/09/17 Javascript
原生JavaScript实现异步多文件上传
2015/12/02 Javascript
浅谈Koa服务限流方法实践
2017/10/23 Javascript
JS Generator 函数的含义与用法实例总结
2020/04/08 Javascript
关于AngularJS中几种Providers的区别总结
2020/05/17 Javascript
python爬虫面试宝典(常见问题)
2018/03/02 Python
python 文件查找及内容匹配方法
2018/10/25 Python
在python 中实现运行多条shell命令
2019/01/07 Python
详解如何从TensorFlow的mnist数据集导出手写体数字图片
2019/08/05 Python
Python Collatz序列实现过程解析
2019/10/12 Python
Python库skimage绘制二值图像代码实例
2020/04/10 Python
python interpolate插值实例
2020/07/06 Python
python openpyxl模块的使用详解
2021/02/25 Python
英国屋顶用品和材料超市:Roofing Supplies UK
2019/08/24 全球购物
行政助理求职自荐信
2013/10/26 职场文书
助人为乐表扬信范文
2014/01/14 职场文书
商务英语专业求职信范文
2014/01/28 职场文书
西北政法大学自主招生自荐信
2014/01/29 职场文书
道德演讲稿
2014/05/21 职场文书
爱护花草树木的标语
2014/06/11 职场文书
酒店采购员岗位职责
2015/04/03 职场文书
外出听课学习心得体会
2016/01/15 职场文书
创业计划书之都市休闲农庄
2019/12/28 职场文书
使用Python的开发框架Brownie部署以太坊智能合约
2021/05/28 Python