如何用react封装一款Tree 树形组件

语言: CN / TW / HK

theme: channing-cyan highlight: night-owl


这是我参与11月更文挑战的第11天,活动详情查看:[2021最后一次更文挑战](https://juejin.cn/post/7023643374569816095/ "https://juejin.cn/post/7023643374569816095/") > TIP 👉 **呜呼!楚虽三户能亡秦,岂有堂堂中国空无人!____陆游《金错刀行》**

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的`轻量,简单`,其实这个时候我们并不想采用第三方的框架。 # Tree 树形控件 ### import ##### 建议用tree2数据,通用后端给的json数据 ```js import Tree from '@/components/Tree/Tree2'; ``` ### Props ##### 1. treeData * 类型:array (必填) * 默认值:[] * 说明:树形数据 ##### 2. iconFolder * 类型:boolean (必填) * 默认值:true / false * 说明:是否有文件夹的折起标志 ##### 3. iconStyle * 类型:boolean (必填) * 默认值:true / false * 说明:是否有加减的折起标志 ##### 4. canSelected * 类型:boolean (必填) * 默认值:true / false * 说明:是否可勾选 ##### 5. clickNodeGetInfo * 类型:func * 默认值:无 * 说明:点击触发回调函数,入参如: * {obj} node 触发树的某个节点 ##### 6. parentCodeName * 类型:string (必填) * 默认值:'parentCode' * 说明:参照后端数据查找父节点值字段,不固定值 ##### 7. codeName * 类型:string (必填) * 默认值:'code' * 说明:参照后端数据查找子节点值字段,不固定值 实现Tree.js ```js import React from 'react'; import './Tree.scss'; /** * Tree 组件所传属性描述 * treeList 树的所有数据 arr * onClick 点击树的执行方法 函数 * loadDataAction 动态加载数据 函数 */ class Tree extends React.Component { constructor(props){ super(props); this.handleClick = this.handleClick.bind(this); this.state = { node: props.treeList, } } handleClick(e,item) { let Oi = e.target.parentNode.childNodes[0] ; // Oi.style.transform = Oi.style.transform == "rotate(-90deg)" ? "rotate(0deg)" : "rotate(-90deg)"; this.digui(this.state.node,item); this.props.onClick && this.props.onClick(item); this.props.loadDataAction && this.props.loadDataAction(item); } checkAction(item){ item.checked = !item.checked; this.setState({ node : this.state.node }); // console.log(item); } digui(node,item){ let newNode = node; for(let i=0; i {item.title} ) }else{ return ( {this.handleClick(e,item)}}>{item.title} ) } } tree(child){ let treeItem; // 如果有子元素 if(child){ // 子元素是数组的形式,把所有的子元素循环出来 treeItem = child.map((item, key) => { // 同理,设置样式 let itemStyle = { paddingLeft: 20*parseInt(item.level.slice(5)-1)+'px', position:'relative' }; let wrapperStyle = { left: 5*parseInt(item.level.slice(5)-1)+'px', }; // 同理,设置➡️ let iconChevron; if(item.child){ if(item.open){ iconChevron = 'icon icon-xiangxiazuocedaohang'; }else{ iconChevron = 'icon icon-xiangyouzuocedaohang'; } }else{ iconChevron = 'icon tree-icon'; } return (
  • { this.props.loadDataAction ? ( (!(item.child && item.child.length)) ? {this.handleClick(e,item)}}> : ( this.props.notice ?
    : {this.handleClick(e,item)}}> ) ): {this.handleClick(e,item)}}> } { this.props.type == 'check' && } {this.itemTitle(item)}
  • {/* 如果当前子元素还有子元素,就递归使用tree方法,把当前子元素的子元素渲染出来 */} {item.open && this.tree(item.child)}
) }) } return treeItem; } render() { return (
{/*{this.tree(this.state.node)}*/} {this.tree(this.props.treeList)}
); } } export default Tree; ``` 样式这块就先不放了 「欢迎在评论区讨论」 #### 希望看完的朋友可以给个赞,鼓励一下